balena_cdsl/output/serialization/
object_types.rs

1use std::collections::HashMap;
2use std::string::ToString;
3
4use heck::MixedCase;
5use serde::ser::Error;
6use serde::ser::SerializeMap;
7use serde::Serialize;
8use serde::Serializer;
9
10use crate::dsl::schema::object_types::bounds::ArrayItemObjectBounds;
11use crate::dsl::schema::object_types::bounds::ArrayObjectBounds;
12use crate::dsl::schema::object_types::bounds::ArrayUniqueItemBound;
13use crate::dsl::schema::object_types::bounds::DefaultValue;
14use crate::dsl::schema::object_types::bounds::EnumerationValue;
15use crate::dsl::schema::object_types::bounds::IntegerBound;
16use crate::dsl::schema::object_types::bounds::IntegerObjectBounds;
17use crate::dsl::schema::object_types::bounds::StringLength;
18use crate::dsl::schema::object_types::bounds::StringObjectBounds;
19use crate::dsl::schema::object_types::RawObjectType;
20use crate::dsl::schema::object_types::ObjectType;
21use crate::dsl::schema::object_types::bounds::BooleanObjectBounds;
22use crate::dsl::schema::KeysValues;
23
24impl Serialize for EnumerationValue {
25    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
26    where
27        S: Serializer,
28    {
29        let mut map = serializer.serialize_map(None)?;
30        if self.annotations.title.is_some() {
31            map.serialize_entry("title", &self.annotations.title)?;
32            map.serialize_entry("enum", &vec![&self.value])?;
33        }
34
35        map.end()
36    }
37}
38
39pub fn serialize_object_type<O, E, S>(object_type: &ObjectType, map: &mut S) -> Result<(), E>
40where
41    E: Error,
42    S: SerializeMap<Ok = O, Error = E>,
43{
44    let raw_type = object_type.inner_raw();
45    let default = object_type.data().default_value();
46
47    map.serialize_entry("type", object_type_name(raw_type))?;
48    serialize_default(default, map)?;
49
50    match raw_type {
51        RawObjectType::Object => {
52            map.serialize_entry(
53                "additionalProperties",
54                &object_type.data().allow_additional_properties(),
55            )?;
56        }
57        RawObjectType::Boolean(object_bounds) => serialize_boolean(object_bounds, map)?,
58        RawObjectType::String(object_bounds) => serialize_string(object_bounds, map)?,
59        RawObjectType::Text(object_bounds) => serialize_string(object_bounds, map)?,
60        RawObjectType::Password(object_bounds) => serialize_password(object_bounds, map)?,
61        RawObjectType::Integer(object_bounds) => serialize_integer(object_bounds, map)?,
62        RawObjectType::Number(object_bounds) => serialize_integer(object_bounds, map)?,
63        RawObjectType::Port(object_bounds) => serialize_integer(object_bounds, map)?,
64        RawObjectType::Array(object_bounds) => serialize_array(object_bounds, map)?,
65        RawObjectType::Stringlist(object_bounds) => serialize_array(object_bounds, map)?,
66
67        RawObjectType::Hostname(object_bounds) => serialize_string_with_format("hostname", object_bounds, map)?,
68        RawObjectType::Datetime(object_bounds) => serialize_string_with_format("date-time", object_bounds, map)?,
69        RawObjectType::Date(object_bounds) => serialize_string_with_format("date", object_bounds, map)?,
70        RawObjectType::Time(object_bounds) => serialize_string_with_format("time", object_bounds, map)?,
71        RawObjectType::Email(object_bounds) => serialize_string_with_format("email", object_bounds, map)?,
72        RawObjectType::IPV4(object_bounds) => serialize_string_with_format("ipv4", object_bounds, map)?,
73        RawObjectType::IPV6(object_bounds) => serialize_string_with_format("ipv6", object_bounds, map)?,
74        RawObjectType::URI(object_bounds) => serialize_string_with_format("uri", object_bounds, map)?,
75        RawObjectType::File(object_bounds) => serialize_string_with_format("data-url", object_bounds, map)?,
76
77        RawObjectType::DnsmasqAddress(object_bounds) => {
78            serialize_string_with_format("dnsmasq-address", object_bounds, map)?
79        }
80        RawObjectType::ChronyAddress(object_bounds) => {
81            serialize_string_with_format("chrony-address", object_bounds, map)?
82        }
83        RawObjectType::IpTablesAddress(object_bounds) => {
84            serialize_string_with_format("iptables-address", object_bounds, map)?
85        }
86    };
87    Ok(())
88}
89
90pub fn object_type_name(object_type: &RawObjectType) -> &str {
91    match object_type {
92        RawObjectType::Object => "object",
93        RawObjectType::Boolean(_) => "boolean",
94        RawObjectType::String(_) => "string",
95        RawObjectType::Text(_) => "string",
96        RawObjectType::Password(_) => "string",
97        RawObjectType::Integer(_) => "integer",
98        RawObjectType::Number(_) => "number",
99        RawObjectType::Port(_) => "integer",
100        RawObjectType::Array(_) => "array",
101        RawObjectType::Stringlist(_) => "array",
102
103        RawObjectType::Hostname(_) => "string",
104        RawObjectType::Datetime(_) => "string",
105        RawObjectType::Date(_) => "string",
106        RawObjectType::Time(_) => "string",
107        RawObjectType::Email(_) => "string",
108        RawObjectType::IPV4(_) => "string",
109        RawObjectType::IPV6(_) => "string",
110        RawObjectType::URI(_) => "string",
111
112        RawObjectType::File(_) => "string",
113
114        RawObjectType::DnsmasqAddress(_) => "string",
115        RawObjectType::ChronyAddress(_) => "string",
116        RawObjectType::IpTablesAddress(_) => "string",
117    }
118}
119
120pub fn serialize_keys_values<O, E, S>(keys_values: &KeysValues, map: &mut S) -> Result<(), E>
121where
122    E: Error,
123    S: SerializeMap<Ok = O, Error = E>,
124{
125    let mut pattern_properties = HashMap::new();
126    pattern_properties.insert(keys_values.keys.pattern.to_string(), &keys_values.values);
127    map.serialize_entry("patternProperties", &pattern_properties)?;
128    Ok(())
129}
130
131fn serialize_default<O, E, S>(default: &Option<DefaultValue>, map: &mut S) -> Result<(), E>
132where
133    E: Error,
134    S: SerializeMap<Ok = O, Error = E>,
135{
136    for value in default {
137        map.serialize_entry("default", value.value())?;
138    }
139    Ok(())
140}
141
142fn serialize_boolean<O, E, S>(bounds: &Option<BooleanObjectBounds>, map: &mut S) -> Result<(), E>
143where
144    E: Error,
145    S: SerializeMap<Ok = O, Error = E>,
146{
147    if let Some(bounds) = bounds {
148        serialize_enum_bounds(&bounds.0, map)?;
149    }
150    Ok(())
151}
152
153fn serialize_string<O, E, S>(bounds: &Option<StringObjectBounds>, map: &mut S) -> Result<(), E>
154where
155    E: Error,
156    S: SerializeMap<Ok = O, Error = E>,
157{
158    for enumeration_values in bounds {
159        serialize_string_bounds(&enumeration_values, map)?;
160    }
161    Ok(())
162}
163
164fn serialize_string_with_format<O, E, S>(
165    format: &str,
166    bounds: &Option<StringObjectBounds>,
167    map: &mut S,
168) -> Result<(), E>
169where
170    E: Error,
171    S: SerializeMap<Ok = O, Error = E>,
172{
173    map.serialize_entry("format", format)?;
174    serialize_string(bounds, map)
175}
176
177fn serialize_password<O, E, S>(bounds: &Option<StringObjectBounds>, map: &mut S) -> Result<(), E>
178where
179    E: Error,
180    S: SerializeMap<Ok = O, Error = E>,
181{
182    map.serialize_entry("writeOnly", &true)?;
183    serialize_string(bounds, map)?;
184    Ok(())
185}
186
187fn serialize_integer_bound<O, E, S>(name: &str, bound: &Option<IntegerBound>, map: &mut S) -> Result<(), E>
188where
189    E: Error,
190    S: SerializeMap<Ok = O, Error = E>,
191{
192    if let Some(value) = bound {
193        match value {
194            IntegerBound::Inclusive(value) => map.serialize_entry(name, &value)?,
195            IntegerBound::Exclusive(value) => {
196                map.serialize_entry(name, &value)?;
197                map.serialize_entry(&("exclusive ".to_string() + name).to_mixed_case(), &true)?;
198            }
199        }
200    }
201    Ok(())
202}
203
204fn serialize_array<O, E, S>(bounds: &Option<ArrayObjectBounds>, map: &mut S) -> Result<(), E>
205where
206    E: Error,
207    S: SerializeMap<Ok = O, Error = E>,
208{
209    if let Some(bounds) = bounds {
210        if let Some(max) = bounds.maximum_number_of_items {
211            map.serialize_entry("maxItems", &max)?;
212        }
213        if let Some(min) = bounds.minimum_number_of_items {
214            map.serialize_entry("minItems", &min)?;
215        }
216        if let Some(ref items) = bounds.items {
217            match items {
218                ArrayItemObjectBounds::AllItems(schema) => {
219                    map.serialize_entry("items", &schema)?;
220                }
221                ArrayItemObjectBounds::AnyItems(schemas) => {
222                    let mut wrapper = HashMap::new();
223                    wrapper.insert("oneOf", schemas);
224                    map.serialize_entry("items", &wrapper)?;
225                }
226            }
227        }
228        if let Some(ref unique_items) = bounds.unique_items {
229            match unique_items {
230                ArrayUniqueItemBound::All => {
231                    map.serialize_entry("uniqueItems", &true)?;
232                }
233                ArrayUniqueItemBound::Specific(items) => {
234                    map.serialize_entry("$$uniqueItemProperties", items)?;
235                }
236            }
237        }
238    }
239    Ok(())
240}
241
242fn serialize_integer<O, E, S>(bounds: &Option<IntegerObjectBounds>, map: &mut S) -> Result<(), E>
243where
244    E: Error,
245    S: SerializeMap<Ok = O, Error = E>,
246{
247    if let Some(bounds) = bounds {
248        match bounds {
249            IntegerObjectBounds::Conditions(conditions) => {
250                serialize_integer_bound("maximum", &conditions.maximum, map)?;
251                serialize_integer_bound("minimum", &conditions.minimum, map)?;
252                if let Some(multiple_of) = conditions.multiple_of {
253                    map.serialize_entry("multipleOf", &multiple_of)?;
254                }
255            }
256            IntegerObjectBounds::Enumeration(list) => serialize_enum_bounds(list, map)?,
257        }
258    }
259    Ok(())
260}
261
262fn serialize_string_bounds<O, E, S>(string_bounds: &StringObjectBounds, map: &mut S) -> Result<(), E>
263where
264    E: Error,
265    S: SerializeMap<Ok = O, Error = E>,
266{
267    match string_bounds {
268        StringObjectBounds::Enumeration(values) => serialize_enum_bounds(values, map)?,
269        StringObjectBounds::Value(value_bounds) => {
270            if let Some(pattern) = &value_bounds.pattern {
271                map.serialize_entry("pattern", pattern.as_str())?
272            }
273            if let Some(length) = &value_bounds.length {
274                serialize_length_bounds(length, map)?
275            }
276        }
277    }
278    Ok(())
279}
280
281fn serialize_enum_bounds<O, E, S>(values: &[EnumerationValue], map: &mut S) -> Result<(), E>
282where
283    E: Error,
284    S: SerializeMap<Ok = O, Error = E>,
285{
286    if values.len() == 1 {
287        serialize_singular_constant_value(&values[0], map)?;
288    } else {
289        serialize_multiple_enum_values(&values, map)?;
290    }
291    Ok(())
292}
293
294fn serialize_length_bounds<O, E, S>(length_bounds: &StringLength, map: &mut S) -> Result<(), E>
295where
296    E: Error,
297    S: SerializeMap<Ok = O, Error = E>,
298{
299    if let Some(maximum) = length_bounds.maximum {
300        map.serialize_entry("maxLength", &maximum)?;
301    }
302    if let Some(minimum) = length_bounds.minimum {
303        map.serialize_entry("minLength", &minimum)?;
304    }
305    Ok(())
306}
307
308fn serialize_multiple_enum_values<O, E, S>(enumeration_values: &[EnumerationValue], map: &mut S) -> Result<(), E>
309where
310    E: Error,
311    S: SerializeMap<Ok = O, Error = E>,
312{
313    if !enumeration_values.is_empty() {
314        map.serialize_entry("oneOf", &enumeration_values)?;
315    }
316    Ok(())
317}
318
319fn serialize_singular_constant_value<O, E, S>(constant: &EnumerationValue, map: &mut S) -> Result<(), E>
320where
321    E: Error,
322    S: SerializeMap<Ok = O, Error = E>,
323{
324    Ok(map.serialize_entry("enum", &vec![constant.value.clone()]))?
325}