balena_cdsl/output/serialization/
ui_object.rs

1use std::collections::HashMap;
2use serde::ser::SerializeMap;
3
4use crate::dsl::schema::DocumentRoot;
5use crate::dsl::schema::Schema;
6use crate::dsl::schema::SchemaList;
7use crate::output::UiObject;
8use crate::output::UiObjectProperty;
9use crate::output::UiObjectRoot;
10use serde::Serialize;
11use serde::Serializer;
12use crate::dsl::schema::KeysSchema;
13use crate::output::UiOptions;
14use crate::dsl::schema::Annotations;
15use crate::dsl::schema::Widget;
16use crate::dsl::schema::object_types::RawObjectType;
17
18impl From<DocumentRoot> for UiObjectRoot {
19    fn from(root: DocumentRoot) -> Self {
20        UiObjectRoot(Some(root.schema().into()))
21    }
22}
23
24impl From<SchemaList> for UiObject {
25    fn from(list: SchemaList) -> Self {
26        let ui_object_entries: Vec<(String, UiObjectProperty)> = list
27            .entries()
28            .iter()
29            .filter_map(|entry| {
30                let property: UiObjectProperty = entry.schema.clone().into();
31                if !property.is_empty() {
32                    Some((entry.name.clone(), property))
33                } else {
34                    None
35                }
36            })
37            .collect();
38
39        let ui_object_entries: HashMap<String, UiObjectProperty> = ui_object_entries.into_iter().collect();
40        UiObject(ui_object_entries)
41    }
42}
43
44impl From<Schema> for UiObjectProperty {
45    fn from(schema: Schema) -> Self {
46        let annotations = schema.annotations.clone();
47        let (help, warning, description) = help_warning_description(&annotations);
48        let widget = widget(&schema);
49        let placeholder = placeholder(&annotations);
50        let readonly = readonly(&annotations);
51        let keys_values = schema.dynamic.map(|keys_values| keys_values.keys);
52
53        let children = schema.children;
54        let children_ui_objects = children.clone().map(|children| children.into());
55        let order = children
56            .clone()
57            .map(|list| list.all_names().iter().map(|name| name.to_string()).collect());
58
59        let ui_options = ui_options(&annotations);
60
61        UiObjectProperty {
62            help,
63            warning,
64            description,
65            placeholder,
66            widget,
67            properties: children_ui_objects,
68            keys: keys_values,
69            ui_options,
70            readonly,
71            order,
72        }
73    }
74}
75
76impl Serialize for KeysSchema {
77    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
78    where
79        S: Serializer,
80    {
81        let mut map = serializer.serialize_map(None)?;
82        if let Some(title) = &self.title {
83            let mut keys_definition = HashMap::new();
84            keys_definition.insert("ui:title", title);
85            map.serialize_entry("ui:keys", &keys_definition)?;
86        }
87        map.end()
88    }
89}
90
91fn ui_options(annotations: &Annotations) -> Option<UiOptions> {
92    let default_ui_options = UiOptions::default();
93    let ui_options = UiOptions {
94        removable: annotations.removable.unwrap_or(default_ui_options.removable),
95        addable: annotations.addable.unwrap_or(default_ui_options.addable),
96        orderable: annotations.orderable.unwrap_or(default_ui_options.orderable),
97    };
98    if ui_options == default_ui_options {
99        None
100    } else {
101        Some(ui_options)
102    }
103}
104
105fn widget(schema: &Schema) -> Option<Widget> {
106    let annotations = &schema.annotations;
107    if let RawObjectType::Password(_) = schema.object_type.inner_raw() {
108        return Some(Widget::Password);
109    }
110    if annotations.writeonly.unwrap_or(false) {
111        return Some(Widget::Password);
112    }
113    if annotations.hidden.unwrap_or(false) {
114        return Some(Widget::Hidden);
115    }
116    annotations.widget.clone()
117}
118
119fn help_warning_description(annotations: &Annotations) -> (Option<String>, Option<String>, Option<String>) {
120    let help = &annotations.help;
121    let warning = &annotations.warning;
122    let description = &annotations.description;
123    (help.clone(), warning.clone(), description.clone())
124}
125
126fn placeholder(annotations: &Annotations) -> Option<String> {
127    let placeholder = &annotations.placeholder;
128    placeholder.clone()
129}
130
131fn readonly(annotations: &Annotations) -> Option<bool> {
132    annotations.readonly
133}