tag2upload_service_manager/
ui_abstract.rs

1
2use crate::prelude::*;
3
4/// Individual values that can be displayed in HTML pages
5pub trait UiDisplay {
6    fn ui_display(&self) -> Cow<'_, str>;
7}
8
9/// Collections of name - value pairs, for HTML pages
10//
11// TODO this should be more flexible.
12//  1. We should return proper JSON values, not strings, when in API
13//     call mode (Accept: application/json).
14//  2. We ought to have a facility for non-string fields even in HTML,
15//     for things that are going to affect the rendering.
16pub trait UiMap {
17    fn ui_serialize(&self) -> HashMap<&str, Cow<'_, str>> {
18        let mut map = HashMap::new();
19        map.extend(self.ui_fields());
20        map
21    }
22
23    fn ui_fields(&self) -> impl Iterator<Item = (&str, Cow<'_, str>)>;
24}
25
26pub struct UiSerializeMap<T>(pub T);
27
28impl<T: UiMap> Serialize for UiSerializeMap<T> {
29    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
30        self.0
31            .ui_serialize()
32            .serialize(ser)
33    }
34}
35
36pub struct UiSerializeRows<R>(pub Vec<R>);
37
38impl<R: UiMap> Serialize for UiSerializeRows<R> {
39    fn serialize<S: Serializer>(&self, ser: S) -> Result<S::Ok, S::Error> {
40        self.0.iter().map(|row| row.ui_serialize())
41            .collect_vec()
42            .serialize(ser)
43    }
44}
45
46define_derive_deftly! {
47    /// Implement `UiMap`
48    ///
49    /// Attributess:
50    ///  * **`#[deftly(ui(flatten))]`**
51    ///  * **`#[deftly(ui(skip))]`**
52    UiMap expect items:
53
54    impl $crate::ui_abstract::UiMap for $ttype {
55        fn ui_fields(&self) -> impl Iterator<Item = (&str, Cow<'_, str>)> {
56            use $crate::ui_abstract::*;
57
58            chain!(
59                $(
60                    ${select1
61                      fmeta(ui(flatten)) {
62                          <$ftype as UiMap>::ui_fields(&self.$fname),
63                      }
64                      fmeta(ui(skip)) {
65                      }
66                      else {
67                          [(
68                              stringify!($fname),
69                              <$ftype as UiDisplay>::ui_display(&self.$fname),
70                          )],
71                      }
72                    }
73                )
74            )
75        }
76    }
77}
78
79define_derive_deftly! {
80    /// Implement `UiDisplay` for an enum of unit variants
81    UiDisplayEnum for enum, expect items:
82
83    impl $crate::ui_abstract::UiDisplay for $ttype {
84        fn ui_display(&self) -> Cow<'_, str> {
85            match self {
86                $(
87                  ${if v_is_unit {
88                    $tname::$vname {} => Cow::Borrowed(stringify!($vname))
89                  } else {
90                    $tname::$vname(inner) => {
91                        $crate::ui_abstract::UiDisplay::ui_display(inner)
92                    }
93                  }},
94                )
95            }
96        }
97    }
98}
99
100impl UiDisplay for String {
101    fn ui_display(&self) -> Cow<'_, str> { Cow::Borrowed(self) }
102}
103
104impl<T: UiDisplay + SuitableForNoneIsEmpty> UiDisplay for NoneIsEmpty<T> {
105    fn ui_display(&self) -> Cow<'_, str> {
106        self.as_ref().map(|v| v.ui_display()).unwrap_or_default()
107    }
108}
109
110impl<T: UiDisplay> UiDisplay for Option<T> {
111    fn ui_display(&self) -> Cow<'_, str> {
112        self.as_ref().map(|v| v.ui_display()).unwrap_or_default()
113    }
114}
115
116/// Implement `UiDisplay` in terms of `ToString`
117#[macro_export]
118macro_rules! ui_display_via_to_string { { $( $ty:ty )* } => { $(
119    impl $crate::ui_abstract::UiDisplay for $ty {
120        fn ui_display(&self) -> Cow<'_, str> { Cow::Owned(self.to_string()) }
121    }
122)* } }
123
124ui_display_via_to_string! { bool u32 i64 }