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}
194
195impl SettingValue {
196 fn raw_value(&self) -> &str {
197 match self {
198 Self::Group { .. } => "group",
199 Self::Select { .. } => "select",
200 Self::MultiSelect { .. } => "multi-select",
201 Self::Toggle { .. } => "switch",
202 Self::Stepper { .. } => "stepper",
203 Self::Segment { .. } => "segment",
204 Self::Text { .. } => "text",
205 Self::Button => "button",
206 Self::Link { .. } => "link",
207 Self::Login { .. } => "login",
208 Self::Page { .. } => "page",
209 Self::EditableList { .. } => "editable-list",
210 }
211 }
212}
213
214#[derive(Debug, Clone, PartialEq)]
215pub enum PageIcon {
216 System {
217 name: String,
218 color: String,
219 inset: Option<i32>,
220 },
221 Url(String),
222}
223
224impl Serialize for PageIcon {
225 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
226 where
227 S: serde::Serializer,
228 {
229 let mut state = serializer.serialize_struct(
230 "Setting",
231 match self {
232 Self::System { .. } => 2,
233 Self::Url(_) => 1,
234 },
235 )?;
236 match self {
237 Self::System { name, color, inset } => {
238 state.serialize_field("type", "system")?;
239 state.serialize_field("name", name)?;
240 state.serialize_field("color", color)?;
241 state.serialize_field("inset", inset)?;
242 }
243 Self::Url(url) => {
244 state.serialize_field("type", "url")?;
245 state.serialize_field("url", url)?;
246 }
247 }
248 state.end()
249 }
250}
251
252macro_rules! create_setting_struct {
253 (
254 $struct_name:ident,
255 $setting_kind:ident,
256 $doc_comment:expr,
257 { $($(#[$field_meta:meta])* $field_name:ident: $field_type:ty),* $(,)? },
258 { $($def_field_name:ident: $default_value:expr),* $(,)? }
259 ) => {
260 #[doc = $doc_comment]
261 #[derive(Debug, Clone, PartialEq)]
264 pub struct $struct_name {
265 pub key: Cow<'static, str>,
267 pub title: Cow<'static, str>,
269 pub notification: Option<Cow<'static, str>>,
271 pub requires: Option<Cow<'static, str>>,
273 pub requires_false: Option<Cow<'static, str>>,
275 pub refreshes: Option<Vec<Cow<'static, str>>>,
283 $(
284 $(#[$field_meta])*
285 pub $field_name: $field_type
286 ),*
287 }
288
289 impl From<$struct_name> for Setting {
290 fn from(source: $struct_name) -> Self {
291 Setting {
292 key: source.key,
293 title: source.title,
294 notification: source.notification,
295 requires: source.requires,
296 requires_false: source.requires_false,
297 refreshes: source.refreshes,
298 value: SettingValue::$setting_kind {
299 $($field_name: source.$field_name),*
300 },
301 }
302 }
303 }
304
305 impl Default for $struct_name {
306 fn default() -> Self {
307 Self {
308 key: Cow::Borrowed(stringify!($struct_name)),
309 title: Cow::Borrowed(stringify!($struct_name)),
310 notification: None,
311 requires: None,
312 requires_false: None,
313 refreshes: None,
314 $($def_field_name: $default_value),*
315 }
316 }
317 }
318 };
319}
320
321create_setting_struct!(
322 GroupSetting,
323 Group,
324 "A group of settings.",
325 {
326 footer: Option<Cow<'static, str>>,
328 items: Vec<Setting>,
330 },
331 {
332 footer: None,
333 items: Vec::new(),
334 }
335);
336
337create_setting_struct!(
338 SelectSetting,
339 Select,
340 "A page that allows selection of a single value.",
341 {
342 values: Vec<Cow<'static, str>>,
344 titles: Option<Vec<Cow<'static, str>>>,
346 auth_to_open: Option<bool>,
348 default: Option<String>,
350 },
351 {
352 values: Vec::new(),
353 titles: None,
354 auth_to_open: None,
355 default: None,
356 }
357);
358
359create_setting_struct!(
360 MultiSelectSetting,
361 MultiSelect,
362 "A page that allows selection of multiple values.",
363 {
364 values: Vec<Cow<'static, str>>,
366 titles: Option<Vec<Cow<'static, str>>>,
368 auth_to_open: Option<bool>,
370 default: Option<Vec<String>>,
372 },
373 {
374 values: Vec::new(),
375 titles: None,
376 auth_to_open: None,
377 default: None,
378 }
379);
380
381create_setting_struct!(
382 ToggleSetting,
383 Toggle,
384 "A toggle switch.",
385 {
386 subtitle: Option<Cow<'static, str>>,
388 auth_to_disable: Option<bool>,
390 default: bool,
392 },
393 {
394 subtitle: None,
395 auth_to_disable: None,
396 default: false,
397 }
398);
399
400create_setting_struct!(
401 StepperSetting,
402 Stepper,
403 "A numeric stepper control.",
404 {
405 minimum_value: f64,
407 maximum_value: f64,
409 step_value: Option<f64>,
411 default: Option<f64>,
413 },
414 {
415 minimum_value: 1.0,
416 maximum_value: 10.0,
417 step_value: None,
418 default: None,
419 }
420);
421
422create_setting_struct!(
423 SegmentSetting,
424 Segment,
425 "A segmented control.",
426 {
427 options: Vec<Cow<'static, str>>,
429 default: Option<i32>,
431 },
432 {
433 options: Vec::new(),
434 default: None,
435 }
436);
437
438create_setting_struct!(
439 TextSetting,
440 Text,
441 "A text input field.",
442 {
443 placeholder: Option<Cow<'static, str>>,
445 autocapitalization_type: Option<i32>,
447 keyboard_type: Option<i32>,
449 return_key_type: Option<i32>,
451 autocorrection_disabled: Option<bool>,
453 secure: Option<bool>,
455 default: Option<Cow<'static, str>>,
457 },
458 {
459 placeholder: None,
460 autocapitalization_type: None,
461 keyboard_type: None,
462 return_key_type: None,
463 autocorrection_disabled: None,
464 secure: None,
465 default: None,
466 }
467);
468
469create_setting_struct!(
470 LinkSetting,
471 Link,
472 "A link to a URL.",
473 {
474 url: Cow<'static, str>,
476 external: Option<bool>,
478 },
479 {
480 url: "".into(),
481 external: None,
482 }
483);
484
485create_setting_struct!(
486 LoginSetting,
487 Login,
488 "A login control.",
489 {
490 method: LoginMethod,
492 url: Option<Cow<'static, str>>,
494 url_key: Option<Cow<'static, str>>,
496 logout_title: Option<Cow<'static, str>>,
498 pkce: bool,
500 token_url: Option<Cow<'static, str>>,
502 callback_scheme: Option<Cow<'static, str>>,
504 use_email: bool,
506 local_storage_keys: Option<Vec<String>>,
508 },
509 {
510 method: LoginMethod::OAuth,
511 url: None,
512 url_key: None,
513 logout_title: None,
514 pkce: false,
515 token_url: None,
516 callback_scheme: None,
517 use_email: false,
518 local_storage_keys: None,
519 }
520);
521
522create_setting_struct!(
523 PageSetting,
524 Page,
525 "A page of settings.",
526 {
527 items: Vec<Setting>,
529 inline_title: Option<bool>,
531 auth_to_open: Option<bool>,
533 icon: Option<PageIcon>,
535 info: Option<String>,
537 },
538 {
539 items: Vec::new(),
540 inline_title: None,
541 auth_to_open: None,
542 icon: None,
543 info: None,
544 }
545);
546
547create_setting_struct!(
548 EditableListSetting,
549 EditableList,
550 "A list that can be edited by the user.",
551 {
552 line_limit: Option<i32>,
554 inline: bool,
556 placeholder: Option<Cow<'static, str>>,
558 default: Option<Vec<Cow<'static, str>>>,
560 },
561 {
562 line_limit: None,
563 inline: false,
564 placeholder: None,
565 default: None,
566 }
567);
568
569#[derive(Debug, Clone, PartialEq)]
573pub struct ButtonSetting {
574 pub key: Cow<'static, str>,
576 pub title: Cow<'static, str>,
578 pub notification: Option<Cow<'static, str>>,
580 pub requires: Option<Cow<'static, str>>,
582 pub requires_false: Option<Cow<'static, str>>,
584 pub refreshes: Option<Vec<Cow<'static, str>>>,
586}
587
588impl From<ButtonSetting> for Setting {
589 fn from(button: ButtonSetting) -> Self {
590 Setting {
591 key: button.key,
592 title: button.title,
593 notification: button.notification,
594 requires: button.requires,
595 requires_false: button.requires_false,
596 refreshes: button.refreshes,
597 value: SettingValue::Button,
598 }
599 }
600}
601
602impl Default for ButtonSetting {
603 fn default() -> Self {
604 Self {
605 key: Cow::Borrowed(stringify!($struct_name)),
606 title: Cow::Borrowed(stringify!($struct_name)),
607 notification: None,
608 requires: None,
609 requires_false: None,
610 refreshes: None,
611 }
612 }
613}