1use serde::{Serialize, ser::SerializeStruct};
2
3extern crate alloc;
4use alloc::{borrow::Cow, string::String, vec::Vec};
5
6#[derive(Debug, Clone, PartialEq)]
11pub struct Setting {
12 pub key: Cow<'static, str>,
13 pub title: Cow<'static, str>,
14 pub notification: Option<Cow<'static, str>>,
15 pub requires: Option<Cow<'static, str>>,
16 pub requires_false: Option<Cow<'static, str>>,
17 pub refreshes: Option<Vec<Cow<'static, str>>>,
18 pub value: SettingValue,
19}
20
21impl Serialize for Setting {
22 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
23 where
24 S: serde::Serializer,
25 {
26 let mut state = serializer.serialize_struct("Setting", 8)?;
27 state.serialize_field("type", &self.value.raw_value())?;
28 state.serialize_field("key", &self.key)?;
29 state.serialize_field("title", &self.title)?;
30 state.serialize_field("notification", &self.notification)?;
31 state.serialize_field("requires", &self.requires)?;
32 state.serialize_field("requires_false", &self.requires_false)?;
33 state.serialize_field("refreshes", &self.refreshes)?;
34 state.serialize_field("value", &self.value)?;
35 state.end()
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum LoginMethod {
42 Basic,
44 OAuth,
46 Web,
48}
49
50impl Serialize for LoginMethod {
51 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
52 where
53 S: serde::Serializer,
54 {
55 match self {
56 Self::Basic => serializer.serialize_str("basic"),
57 Self::OAuth => serializer.serialize_str("oauth"),
58 Self::Web => serializer.serialize_str("web"),
59 }
60 }
61}
62
63#[derive(Debug, Clone, PartialEq, Serialize)]
65pub enum SettingValue {
66 Group {
68 footer: Option<Cow<'static, str>>,
70 items: Vec<Setting>,
72 },
73 Select {
75 values: Vec<Cow<'static, str>>,
77 titles: Option<Vec<Cow<'static, str>>>,
79 auth_to_open: Option<bool>,
81 default: Option<String>,
83 },
84 MultiSelect {
86 values: Vec<Cow<'static, str>>,
88 titles: Option<Vec<Cow<'static, str>>>,
90 auth_to_open: Option<bool>,
92 default: Option<Vec<String>>,
94 },
95 Toggle {
97 subtitle: Option<Cow<'static, str>>,
99 auth_to_disable: Option<bool>,
101 default: bool,
103 },
104 Stepper {
106 minimum_value: f64,
108 maximum_value: f64,
110 step_value: Option<f64>,
112 default: Option<f64>,
114 },
115 Segment {
117 options: Vec<Cow<'static, str>>,
119 default: Option<i32>,
121 },
122 Text {
124 placeholder: Option<Cow<'static, str>>,
126 autocapitalization_type: Option<i32>,
128 autocorrection_disabled: Option<bool>,
130 keyboard_type: Option<i32>,
132 return_key_type: Option<i32>,
134 secure: Option<bool>,
136 default: Option<Cow<'static, str>>,
138 },
139 Button,
141 Link {
143 url: Cow<'static, str>,
145 external: Option<bool>,
147 },
148 Login {
150 method: LoginMethod,
152 url: Option<Cow<'static, str>>,
154 url_key: Option<Cow<'static, str>>,
156 logout_title: Option<Cow<'static, str>>,
158 pkce: bool,
160 token_url: Option<Cow<'static, str>>,
162 callback_scheme: Option<Cow<'static, str>>,
164 use_email: bool,
166 local_storage_keys: Option<Vec<String>>,
168 clear_cookies_on_log_out: bool,
170 },
171 Page {
173 items: Vec<Setting>,
175 inline_title: Option<bool>,
177 auth_to_open: Option<bool>,
179 icon: Option<PageIcon>,
181 info: Option<String>,
183 },
184 EditableList {
186 line_limit: Option<i32>,
188 inline: bool,
190 placeholder: Option<Cow<'static, str>>,
192 default: Option<Vec<Cow<'static, str>>>,
194 },
195 Picker {
197 values: Vec<Cow<'static, str>>,
199 titles: Option<Vec<Cow<'static, str>>>,
201 default: Option<String>,
203 },
204}
205
206impl SettingValue {
207 fn raw_value(&self) -> &str {
208 match self {
209 Self::Group { .. } => "group",
210 Self::Select { .. } => "select",
211 Self::MultiSelect { .. } => "multi-select",
212 Self::Toggle { .. } => "switch",
213 Self::Stepper { .. } => "stepper",
214 Self::Segment { .. } => "segment",
215 Self::Text { .. } => "text",
216 Self::Button => "button",
217 Self::Link { .. } => "link",
218 Self::Login { .. } => "login",
219 Self::Page { .. } => "page",
220 Self::EditableList { .. } => "editable-list",
221 Self::Picker { .. } => "picker",
222 }
223 }
224}
225
226#[derive(Debug, Clone, PartialEq)]
227pub enum PageIcon {
228 System {
229 name: String,
230 color: String,
231 inset: Option<i32>,
232 },
233 Url(String),
234}
235
236impl Serialize for PageIcon {
237 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
238 where
239 S: serde::Serializer,
240 {
241 let mut state = serializer.serialize_struct(
242 "Setting",
243 match self {
244 Self::System { .. } => 2,
245 Self::Url(_) => 1,
246 },
247 )?;
248 match self {
249 Self::System { name, color, inset } => {
250 state.serialize_field("type", "system")?;
251 state.serialize_field("name", name)?;
252 state.serialize_field("color", color)?;
253 state.serialize_field("inset", inset)?;
254 }
255 Self::Url(url) => {
256 state.serialize_field("type", "url")?;
257 state.serialize_field("url", url)?;
258 }
259 }
260 state.end()
261 }
262}
263
264macro_rules! create_setting_struct {
265 (
266 $struct_name:ident,
267 $setting_kind:ident,
268 $doc_comment:expr,
269 { $($(#[$field_meta:meta])* $field_name:ident: $field_type:ty),* $(,)? },
270 { $($def_field_name:ident: $default_value:expr),* $(,)? }
271 ) => {
272 #[doc = $doc_comment]
273 #[derive(Debug, Clone, PartialEq)]
276 pub struct $struct_name {
277 pub key: Cow<'static, str>,
279 pub title: Cow<'static, str>,
281 pub notification: Option<Cow<'static, str>>,
283 pub requires: Option<Cow<'static, str>>,
285 pub requires_false: Option<Cow<'static, str>>,
287 pub refreshes: Option<Vec<Cow<'static, str>>>,
295 $(
296 $(#[$field_meta])*
297 pub $field_name: $field_type
298 ),*
299 }
300
301 impl From<$struct_name> for Setting {
302 fn from(source: $struct_name) -> Self {
303 Setting {
304 key: source.key,
305 title: source.title,
306 notification: source.notification,
307 requires: source.requires,
308 requires_false: source.requires_false,
309 refreshes: source.refreshes,
310 value: SettingValue::$setting_kind {
311 $($field_name: source.$field_name),*
312 },
313 }
314 }
315 }
316
317 impl Default for $struct_name {
318 fn default() -> Self {
319 Self {
320 key: Cow::Borrowed(stringify!($struct_name)),
321 title: Cow::Borrowed(stringify!($struct_name)),
322 notification: None,
323 requires: None,
324 requires_false: None,
325 refreshes: None,
326 $($def_field_name: $default_value),*
327 }
328 }
329 }
330 };
331}
332
333create_setting_struct!(
334 GroupSetting,
335 Group,
336 "A group of settings.",
337 {
338 footer: Option<Cow<'static, str>>,
340 items: Vec<Setting>,
342 },
343 {
344 footer: None,
345 items: Vec::new(),
346 }
347);
348
349create_setting_struct!(
350 SelectSetting,
351 Select,
352 "A page that allows selection of a single value.",
353 {
354 values: Vec<Cow<'static, str>>,
356 titles: Option<Vec<Cow<'static, str>>>,
358 auth_to_open: Option<bool>,
360 default: Option<String>,
362 },
363 {
364 values: Vec::new(),
365 titles: None,
366 auth_to_open: None,
367 default: None,
368 }
369);
370
371create_setting_struct!(
372 MultiSelectSetting,
373 MultiSelect,
374 "A page that allows selection of multiple values.",
375 {
376 values: Vec<Cow<'static, str>>,
378 titles: Option<Vec<Cow<'static, str>>>,
380 auth_to_open: Option<bool>,
382 default: Option<Vec<String>>,
384 },
385 {
386 values: Vec::new(),
387 titles: None,
388 auth_to_open: None,
389 default: None,
390 }
391);
392
393create_setting_struct!(
394 ToggleSetting,
395 Toggle,
396 "A toggle switch.",
397 {
398 subtitle: Option<Cow<'static, str>>,
400 auth_to_disable: Option<bool>,
402 default: bool,
404 },
405 {
406 subtitle: None,
407 auth_to_disable: None,
408 default: false,
409 }
410);
411
412create_setting_struct!(
413 StepperSetting,
414 Stepper,
415 "A numeric stepper control.",
416 {
417 minimum_value: f64,
419 maximum_value: f64,
421 step_value: Option<f64>,
423 default: Option<f64>,
425 },
426 {
427 minimum_value: 1.0,
428 maximum_value: 10.0,
429 step_value: None,
430 default: None,
431 }
432);
433
434create_setting_struct!(
435 SegmentSetting,
436 Segment,
437 "A segmented control.",
438 {
439 options: Vec<Cow<'static, str>>,
441 default: Option<i32>,
443 },
444 {
445 options: Vec::new(),
446 default: None,
447 }
448);
449
450create_setting_struct!(
451 TextSetting,
452 Text,
453 "A text input field.",
454 {
455 placeholder: Option<Cow<'static, str>>,
457 autocapitalization_type: Option<i32>,
459 keyboard_type: Option<i32>,
461 return_key_type: Option<i32>,
463 autocorrection_disabled: Option<bool>,
465 secure: Option<bool>,
467 default: Option<Cow<'static, str>>,
469 },
470 {
471 placeholder: None,
472 autocapitalization_type: None,
473 keyboard_type: None,
474 return_key_type: None,
475 autocorrection_disabled: None,
476 secure: None,
477 default: None,
478 }
479);
480
481create_setting_struct!(
482 LinkSetting,
483 Link,
484 "A link to a URL.",
485 {
486 url: Cow<'static, str>,
488 external: Option<bool>,
490 },
491 {
492 url: "".into(),
493 external: None,
494 }
495);
496
497create_setting_struct!(
498 LoginSetting,
499 Login,
500 "A login control.",
501 {
502 method: LoginMethod,
504 url: Option<Cow<'static, str>>,
506 url_key: Option<Cow<'static, str>>,
508 logout_title: Option<Cow<'static, str>>,
510 pkce: bool,
512 token_url: Option<Cow<'static, str>>,
514 callback_scheme: Option<Cow<'static, str>>,
516 use_email: bool,
518 local_storage_keys: Option<Vec<String>>,
520 clear_cookies_on_log_out: bool,
522 },
523 {
524 method: LoginMethod::OAuth,
525 url: None,
526 url_key: None,
527 logout_title: None,
528 pkce: false,
529 token_url: None,
530 callback_scheme: None,
531 use_email: false,
532 local_storage_keys: None,
533 clear_cookies_on_log_out: false,
534 }
535);
536
537create_setting_struct!(
538 PageSetting,
539 Page,
540 "A page of settings.",
541 {
542 items: Vec<Setting>,
544 inline_title: Option<bool>,
546 auth_to_open: Option<bool>,
548 icon: Option<PageIcon>,
550 info: Option<String>,
552 },
553 {
554 items: Vec::new(),
555 inline_title: None,
556 auth_to_open: None,
557 icon: None,
558 info: None,
559 }
560);
561
562create_setting_struct!(
563 EditableListSetting,
564 EditableList,
565 "A list that can be edited by the user.",
566 {
567 line_limit: Option<i32>,
569 inline: bool,
571 placeholder: Option<Cow<'static, str>>,
573 default: Option<Vec<Cow<'static, str>>>,
575 },
576 {
577 line_limit: None,
578 inline: false,
579 placeholder: None,
580 default: None,
581 }
582);
583
584create_setting_struct!(
585 PickerSetting,
586 Picker,
587 "An inline picker that allows selection of a single value.",
588 {
589 values: Vec<Cow<'static, str>>,
591 titles: Option<Vec<Cow<'static, str>>>,
593 default: Option<String>,
595 },
596 {
597 values: Vec::new(),
598 titles: None,
599 default: None,
600 }
601);
602
603#[derive(Debug, Clone, PartialEq)]
607pub struct ButtonSetting {
608 pub key: Cow<'static, str>,
610 pub title: Cow<'static, str>,
612 pub notification: Option<Cow<'static, str>>,
614 pub requires: Option<Cow<'static, str>>,
616 pub requires_false: Option<Cow<'static, str>>,
618 pub refreshes: Option<Vec<Cow<'static, str>>>,
620}
621
622impl From<ButtonSetting> for Setting {
623 fn from(button: ButtonSetting) -> Self {
624 Setting {
625 key: button.key,
626 title: button.title,
627 notification: button.notification,
628 requires: button.requires,
629 requires_false: button.requires_false,
630 refreshes: button.refreshes,
631 value: SettingValue::Button,
632 }
633 }
634}
635
636impl Default for ButtonSetting {
637 fn default() -> Self {
638 Self {
639 key: Cow::Borrowed(stringify!($struct_name)),
640 title: Cow::Borrowed(stringify!($struct_name)),
641 notification: None,
642 requires: None,
643 requires_false: None,
644 refreshes: None,
645 }
646 }
647}