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>>>,
282 $(
283 $(#[$field_meta])*
284 pub $field_name: $field_type
285 ),*
286 }
287
288 impl From<$struct_name> for Setting {
289 fn from(source: $struct_name) -> Self {
290 Setting {
291 key: source.key,
292 title: source.title,
293 notification: source.notification,
294 requires: source.requires,
295 requires_false: source.requires_false,
296 refreshes: source.refreshes,
297 value: SettingValue::$setting_kind {
298 $($field_name: source.$field_name),*
299 },
300 }
301 }
302 }
303
304 impl Default for $struct_name {
305 fn default() -> Self {
306 Self {
307 key: Cow::Borrowed(stringify!($struct_name)),
308 title: Cow::Borrowed(stringify!($struct_name)),
309 notification: None,
310 requires: None,
311 requires_false: None,
312 refreshes: None,
313 $($def_field_name: $default_value),*
314 }
315 }
316 }
317 };
318}
319
320create_setting_struct!(
321 GroupSetting,
322 Group,
323 "A group of settings.",
324 {
325 footer: Option<Cow<'static, str>>,
327 items: Vec<Setting>,
329 },
330 {
331 footer: None,
332 items: Vec::new(),
333 }
334);
335
336create_setting_struct!(
337 SelectSetting,
338 Select,
339 "A page that allows selection of a single value.",
340 {
341 values: Vec<Cow<'static, str>>,
343 titles: Option<Vec<Cow<'static, str>>>,
345 auth_to_open: Option<bool>,
347 default: Option<String>,
349 },
350 {
351 values: Vec::new(),
352 titles: None,
353 auth_to_open: None,
354 default: None,
355 }
356);
357
358create_setting_struct!(
359 MultiSelectSetting,
360 MultiSelect,
361 "A page that allows selection of multiple values.",
362 {
363 values: Vec<Cow<'static, str>>,
365 titles: Option<Vec<Cow<'static, str>>>,
367 auth_to_open: Option<bool>,
369 default: Option<Vec<String>>,
371 },
372 {
373 values: Vec::new(),
374 titles: None,
375 auth_to_open: None,
376 default: None,
377 }
378);
379
380create_setting_struct!(
381 ToggleSetting,
382 Toggle,
383 "A toggle switch.",
384 {
385 subtitle: Option<Cow<'static, str>>,
387 auth_to_disable: Option<bool>,
389 default: bool,
391 },
392 {
393 subtitle: None,
394 auth_to_disable: None,
395 default: false,
396 }
397);
398
399create_setting_struct!(
400 StepperSetting,
401 Stepper,
402 "A numeric stepper control.",
403 {
404 minimum_value: f64,
406 maximum_value: f64,
408 step_value: Option<f64>,
410 default: Option<f64>,
412 },
413 {
414 minimum_value: 1.0,
415 maximum_value: 10.0,
416 step_value: None,
417 default: None,
418 }
419);
420
421create_setting_struct!(
422 SegmentSetting,
423 Segment,
424 "A segmented control.",
425 {
426 options: Vec<Cow<'static, str>>,
428 default: Option<i32>,
430 },
431 {
432 options: Vec::new(),
433 default: None,
434 }
435);
436
437create_setting_struct!(
438 TextSetting,
439 Text,
440 "A text input field.",
441 {
442 placeholder: Option<Cow<'static, str>>,
444 autocapitalization_type: Option<i32>,
446 keyboard_type: Option<i32>,
448 return_key_type: Option<i32>,
450 autocorrection_disabled: Option<bool>,
452 secure: Option<bool>,
454 default: Option<Cow<'static, str>>,
456 },
457 {
458 placeholder: None,
459 autocapitalization_type: None,
460 keyboard_type: None,
461 return_key_type: None,
462 autocorrection_disabled: None,
463 secure: None,
464 default: None,
465 }
466);
467
468create_setting_struct!(
469 LinkSetting,
470 Link,
471 "A link to a URL.",
472 {
473 url: Cow<'static, str>,
475 external: Option<bool>,
477 },
478 {
479 url: "".into(),
480 external: None,
481 }
482);
483
484create_setting_struct!(
485 LoginSetting,
486 Login,
487 "A login control.",
488 {
489 method: LoginMethod,
491 url: Option<Cow<'static, str>>,
493 url_key: Option<Cow<'static, str>>,
495 logout_title: Option<Cow<'static, str>>,
497 pkce: bool,
499 token_url: Option<Cow<'static, str>>,
501 callback_scheme: Option<Cow<'static, str>>,
503 use_email: bool,
505 local_storage_keys: Option<Vec<String>>,
507 },
508 {
509 method: LoginMethod::OAuth,
510 url: None,
511 url_key: None,
512 logout_title: None,
513 pkce: false,
514 token_url: None,
515 callback_scheme: None,
516 use_email: false,
517 local_storage_keys: None,
518 }
519);
520
521create_setting_struct!(
522 PageSetting,
523 Page,
524 "A page of settings.",
525 {
526 items: Vec<Setting>,
528 inline_title: Option<bool>,
530 auth_to_open: Option<bool>,
532 icon: Option<PageIcon>,
534 info: Option<String>,
536 },
537 {
538 items: Vec::new(),
539 inline_title: None,
540 auth_to_open: None,
541 icon: None,
542 info: None,
543 }
544);
545
546create_setting_struct!(
547 EditableListSetting,
548 EditableList,
549 "A list that can be edited by the user.",
550 {
551 line_limit: Option<i32>,
553 inline: bool,
555 placeholder: Option<Cow<'static, str>>,
557 default: Option<Vec<Cow<'static, str>>>,
559 },
560 {
561 line_limit: None,
562 inline: false,
563 placeholder: None,
564 default: None,
565 }
566);
567
568#[derive(Debug, Clone, PartialEq)]
572pub struct ButtonSetting {
573 pub key: Cow<'static, str>,
575 pub title: Cow<'static, str>,
577 pub notification: Option<Cow<'static, str>>,
579 pub requires: Option<Cow<'static, str>>,
581 pub requires_false: Option<Cow<'static, str>>,
583 pub refreshes: Option<Vec<Cow<'static, str>>>,
585}
586
587impl From<ButtonSetting> for Setting {
588 fn from(button: ButtonSetting) -> Self {
589 Setting {
590 key: button.key,
591 title: button.title,
592 notification: button.notification,
593 requires: button.requires,
594 requires_false: button.requires_false,
595 refreshes: button.refreshes,
596 value: SettingValue::Button,
597 }
598 }
599}
600
601impl Default for ButtonSetting {
602 fn default() -> Self {
603 Self {
604 key: Cow::Borrowed(stringify!($struct_name)),
605 title: Cow::Borrowed(stringify!($struct_name)),
606 notification: None,
607 requires: None,
608 requires_false: None,
609 refreshes: None,
610 }
611 }
612}