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 },
169 Page {
171 items: Vec<Setting>,
173 inline_title: Option<bool>,
175 auth_to_open: Option<bool>,
177 icon: Option<PageIcon>,
179 info: Option<String>,
181 },
182 EditableList {
184 line_limit: Option<i32>,
186 inline: bool,
188 placeholder: Option<Cow<'static, str>>,
190 default: Option<Vec<Cow<'static, str>>>,
192 },
193 Picker {
195 values: Vec<Cow<'static, str>>,
197 titles: Option<Vec<Cow<'static, str>>>,
199 default: Option<String>,
201 },
202}
203
204impl SettingValue {
205 fn raw_value(&self) -> &str {
206 match self {
207 Self::Group { .. } => "group",
208 Self::Select { .. } => "select",
209 Self::MultiSelect { .. } => "multi-select",
210 Self::Toggle { .. } => "switch",
211 Self::Stepper { .. } => "stepper",
212 Self::Segment { .. } => "segment",
213 Self::Text { .. } => "text",
214 Self::Button => "button",
215 Self::Link { .. } => "link",
216 Self::Login { .. } => "login",
217 Self::Page { .. } => "page",
218 Self::EditableList { .. } => "editable-list",
219 Self::Picker { .. } => "picker",
220 }
221 }
222}
223
224#[derive(Debug, Clone, PartialEq)]
225pub enum PageIcon {
226 System {
227 name: String,
228 color: String,
229 inset: Option<i32>,
230 },
231 Url(String),
232}
233
234impl Serialize for PageIcon {
235 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
236 where
237 S: serde::Serializer,
238 {
239 let mut state = serializer.serialize_struct(
240 "Setting",
241 match self {
242 Self::System { .. } => 2,
243 Self::Url(_) => 1,
244 },
245 )?;
246 match self {
247 Self::System { name, color, inset } => {
248 state.serialize_field("type", "system")?;
249 state.serialize_field("name", name)?;
250 state.serialize_field("color", color)?;
251 state.serialize_field("inset", inset)?;
252 }
253 Self::Url(url) => {
254 state.serialize_field("type", "url")?;
255 state.serialize_field("url", url)?;
256 }
257 }
258 state.end()
259 }
260}
261
262macro_rules! create_setting_struct {
263 (
264 $struct_name:ident,
265 $setting_kind:ident,
266 $doc_comment:expr,
267 { $($(#[$field_meta:meta])* $field_name:ident: $field_type:ty),* $(,)? },
268 { $($def_field_name:ident: $default_value:expr),* $(,)? }
269 ) => {
270 #[doc = $doc_comment]
271 #[derive(Debug, Clone, PartialEq)]
274 pub struct $struct_name {
275 pub key: Cow<'static, str>,
277 pub title: Cow<'static, str>,
279 pub notification: Option<Cow<'static, str>>,
281 pub requires: Option<Cow<'static, str>>,
283 pub requires_false: Option<Cow<'static, str>>,
285 pub refreshes: Option<Vec<Cow<'static, str>>>,
293 $(
294 $(#[$field_meta])*
295 pub $field_name: $field_type
296 ),*
297 }
298
299 impl From<$struct_name> for Setting {
300 fn from(source: $struct_name) -> Self {
301 Setting {
302 key: source.key,
303 title: source.title,
304 notification: source.notification,
305 requires: source.requires,
306 requires_false: source.requires_false,
307 refreshes: source.refreshes,
308 value: SettingValue::$setting_kind {
309 $($field_name: source.$field_name),*
310 },
311 }
312 }
313 }
314
315 impl Default for $struct_name {
316 fn default() -> Self {
317 Self {
318 key: Cow::Borrowed(stringify!($struct_name)),
319 title: Cow::Borrowed(stringify!($struct_name)),
320 notification: None,
321 requires: None,
322 requires_false: None,
323 refreshes: None,
324 $($def_field_name: $default_value),*
325 }
326 }
327 }
328 };
329}
330
331create_setting_struct!(
332 GroupSetting,
333 Group,
334 "A group of settings.",
335 {
336 footer: Option<Cow<'static, str>>,
338 items: Vec<Setting>,
340 },
341 {
342 footer: None,
343 items: Vec::new(),
344 }
345);
346
347create_setting_struct!(
348 SelectSetting,
349 Select,
350 "A page that allows selection of a single value.",
351 {
352 values: Vec<Cow<'static, str>>,
354 titles: Option<Vec<Cow<'static, str>>>,
356 auth_to_open: Option<bool>,
358 default: Option<String>,
360 },
361 {
362 values: Vec::new(),
363 titles: None,
364 auth_to_open: None,
365 default: None,
366 }
367);
368
369create_setting_struct!(
370 MultiSelectSetting,
371 MultiSelect,
372 "A page that allows selection of multiple values.",
373 {
374 values: Vec<Cow<'static, str>>,
376 titles: Option<Vec<Cow<'static, str>>>,
378 auth_to_open: Option<bool>,
380 default: Option<Vec<String>>,
382 },
383 {
384 values: Vec::new(),
385 titles: None,
386 auth_to_open: None,
387 default: None,
388 }
389);
390
391create_setting_struct!(
392 ToggleSetting,
393 Toggle,
394 "A toggle switch.",
395 {
396 subtitle: Option<Cow<'static, str>>,
398 auth_to_disable: Option<bool>,
400 default: bool,
402 },
403 {
404 subtitle: None,
405 auth_to_disable: None,
406 default: false,
407 }
408);
409
410create_setting_struct!(
411 StepperSetting,
412 Stepper,
413 "A numeric stepper control.",
414 {
415 minimum_value: f64,
417 maximum_value: f64,
419 step_value: Option<f64>,
421 default: Option<f64>,
423 },
424 {
425 minimum_value: 1.0,
426 maximum_value: 10.0,
427 step_value: None,
428 default: None,
429 }
430);
431
432create_setting_struct!(
433 SegmentSetting,
434 Segment,
435 "A segmented control.",
436 {
437 options: Vec<Cow<'static, str>>,
439 default: Option<i32>,
441 },
442 {
443 options: Vec::new(),
444 default: None,
445 }
446);
447
448create_setting_struct!(
449 TextSetting,
450 Text,
451 "A text input field.",
452 {
453 placeholder: Option<Cow<'static, str>>,
455 autocapitalization_type: Option<i32>,
457 keyboard_type: Option<i32>,
459 return_key_type: Option<i32>,
461 autocorrection_disabled: Option<bool>,
463 secure: Option<bool>,
465 default: Option<Cow<'static, str>>,
467 },
468 {
469 placeholder: None,
470 autocapitalization_type: None,
471 keyboard_type: None,
472 return_key_type: None,
473 autocorrection_disabled: None,
474 secure: None,
475 default: None,
476 }
477);
478
479create_setting_struct!(
480 LinkSetting,
481 Link,
482 "A link to a URL.",
483 {
484 url: Cow<'static, str>,
486 external: Option<bool>,
488 },
489 {
490 url: "".into(),
491 external: None,
492 }
493);
494
495create_setting_struct!(
496 LoginSetting,
497 Login,
498 "A login control.",
499 {
500 method: LoginMethod,
502 url: Option<Cow<'static, str>>,
504 url_key: Option<Cow<'static, str>>,
506 logout_title: Option<Cow<'static, str>>,
508 pkce: bool,
510 token_url: Option<Cow<'static, str>>,
512 callback_scheme: Option<Cow<'static, str>>,
514 use_email: bool,
516 local_storage_keys: Option<Vec<String>>,
518 },
519 {
520 method: LoginMethod::OAuth,
521 url: None,
522 url_key: None,
523 logout_title: None,
524 pkce: false,
525 token_url: None,
526 callback_scheme: None,
527 use_email: false,
528 local_storage_keys: None,
529 }
530);
531
532create_setting_struct!(
533 PageSetting,
534 Page,
535 "A page of settings.",
536 {
537 items: Vec<Setting>,
539 inline_title: Option<bool>,
541 auth_to_open: Option<bool>,
543 icon: Option<PageIcon>,
545 info: Option<String>,
547 },
548 {
549 items: Vec::new(),
550 inline_title: None,
551 auth_to_open: None,
552 icon: None,
553 info: None,
554 }
555);
556
557create_setting_struct!(
558 EditableListSetting,
559 EditableList,
560 "A list that can be edited by the user.",
561 {
562 line_limit: Option<i32>,
564 inline: bool,
566 placeholder: Option<Cow<'static, str>>,
568 default: Option<Vec<Cow<'static, str>>>,
570 },
571 {
572 line_limit: None,
573 inline: false,
574 placeholder: None,
575 default: None,
576 }
577);
578
579create_setting_struct!(
580 PickerSetting,
581 Picker,
582 "An inline picker that allows selection of a single value.",
583 {
584 values: Vec<Cow<'static, str>>,
586 titles: Option<Vec<Cow<'static, str>>>,
588 default: Option<String>,
590 },
591 {
592 values: Vec::new(),
593 titles: None,
594 default: None,
595 }
596);
597
598#[derive(Debug, Clone, PartialEq)]
602pub struct ButtonSetting {
603 pub key: Cow<'static, str>,
605 pub title: Cow<'static, str>,
607 pub notification: Option<Cow<'static, str>>,
609 pub requires: Option<Cow<'static, str>>,
611 pub requires_false: Option<Cow<'static, str>>,
613 pub refreshes: Option<Vec<Cow<'static, str>>>,
615}
616
617impl From<ButtonSetting> for Setting {
618 fn from(button: ButtonSetting) -> Self {
619 Setting {
620 key: button.key,
621 title: button.title,
622 notification: button.notification,
623 requires: button.requires,
624 requires_false: button.requires_false,
625 refreshes: button.refreshes,
626 value: SettingValue::Button,
627 }
628 }
629}
630
631impl Default for ButtonSetting {
632 fn default() -> Self {
633 Self {
634 key: Cow::Borrowed(stringify!($struct_name)),
635 title: Cow::Borrowed(stringify!($struct_name)),
636 notification: None,
637 requires: None,
638 requires_false: None,
639 refreshes: None,
640 }
641 }
642}