facet_yaml/serialize/
mod.rs

1//! Create and/or write YAML strings from Rust values.
2
3#[cfg(not(feature = "alloc"))]
4compile_error!("feature `alloc` is required");
5
6mod error;
7
8use core::borrow::Borrow as _;
9
10use alloc::{
11    borrow::Cow,
12    string::{String, ToString as _},
13    vec::Vec,
14};
15
16pub use error::YamlSerError;
17use facet_serialize::{Serialize, Serializer};
18use yaml_rust2::{
19    Yaml, YamlEmitter,
20    yaml::{Array, Hash},
21};
22
23/// Serializer for YAML values.
24pub struct YamlSerializer<'shape> {
25    /// Current stack of where we are in the tree.
26    key_stack: Vec<Cow<'shape, str>>,
27    /// YAML document tree.
28    yaml: Yaml,
29    /// What type the current item is.
30    current: KeyOrValue,
31}
32
33impl<'shape> YamlSerializer<'shape> {
34    /// Create a new serialzer.
35    pub fn new() -> Self {
36        Self {
37            key_stack: Vec::new(),
38            yaml: Yaml::BadValue,
39            current: KeyOrValue::Value,
40        }
41    }
42
43    /// Get the output serialized YAML document.
44    pub fn into_raw_document(self) -> Yaml {
45        self.yaml
46    }
47
48    /// Get the output serialized YAML string.
49    pub fn into_string(self) -> String {
50        let mut output = String::new();
51        let mut emitter = YamlEmitter::new(&mut output);
52        emitter.dump(&self.yaml).unwrap();
53
54        output
55    }
56
57    /// Write a value depending on the context.
58    fn write_value(&mut self, value: Yaml) -> Result<(), YamlSerError> {
59        match self.current {
60            // Write the value
61            KeyOrValue::Value => {
62                if let Some(array) = self.current_mut().as_mut_vec() {
63                    // Push it when it's an array, so we don't have to keep track of its keys
64                    array.push(value);
65                } else if value == Yaml::Null {
66                    // Remove the last item if it's none and a hash value
67                    self.remove_current();
68                } else {
69                    // Convert the pushed value to the yaml type
70                    self.set_current(value);
71                }
72            }
73            // Push the value as a new item
74            KeyOrValue::Key => {
75                let yaml_type = type_name(&value);
76                let map_key = value
77                    .into_string()
78                    .ok_or(YamlSerError::InvalidKeyConversion { yaml_type })?;
79                self.push_key(map_key.into(), "map key");
80            }
81        }
82
83        Ok(())
84    }
85
86    /// Create a new empty item at the key.
87    fn push_key(&mut self, key: Cow<'shape, str>, type_name: &'static str) {
88        #[cfg(feature = "log")]
89        log::trace!("Push {type_name} {}", self.display_full_key());
90        #[cfg(not(feature = "log"))]
91        let _ = type_name;
92
93        // Push into the map
94        self.current_mut()
95            .as_mut_hash()
96            .unwrap()
97            .insert(Yaml::String(key.clone().into_owned()), Yaml::BadValue);
98
99        // Push the key on the stack
100        self.key_stack.push(key);
101    }
102
103    /// Pop the current key, which means the item is finished.
104    fn pop_key(&mut self, type_name: &'static str) -> Option<Cow<'shape, str>> {
105        #[cfg(feature = "log")]
106        log::trace!("Pop {type_name} {}", self.display_full_key());
107        #[cfg(not(feature = "log"))]
108        let _ = type_name;
109
110        self.key_stack.pop()
111    }
112
113    /// Convert the item at the current key to another type.
114    fn set_current(&mut self, yaml: Yaml) {
115        #[cfg(feature = "log")]
116        log::trace!("Set {} to {}", self.display_full_key(), type_name(&yaml));
117
118        *self.current_mut() = yaml;
119    }
120
121    /// Remove the last item.
122    ///
123    /// Item can't be in an array.
124    fn remove_current(&mut self) {
125        let key = self.key_stack.last().unwrap().to_string();
126
127        // Get the second last item
128        let mut item = &mut self.yaml;
129        for key in &self.key_stack[0..self.key_stack.len() - 1] {
130            item = &mut item[key.borrow()];
131        }
132
133        // Remove the current key from it
134        item.as_mut_hash().unwrap().remove(&Yaml::String(key));
135    }
136
137    /// Get the mutable item for the current key.
138    fn current_mut(&'_ mut self) -> &'_ mut Yaml {
139        self.key_stack
140            .iter()
141            .fold(&mut self.yaml, |item, key| &mut item[key.borrow()])
142    }
143
144    /// Print the keys.
145    #[cfg(feature = "log")]
146    fn display_full_key(&self) -> String {
147        if self.key_stack.is_empty() {
148            return "root".to_string();
149        }
150
151        let mut output = "[".to_string();
152        let mut first = true;
153        for key in &self.key_stack {
154            // Only loop over valid keys
155            output = format!("{output}{}{}", if first { "" } else { "." }, key);
156            first = false;
157        }
158        format!("{output}]")
159    }
160}
161
162impl<'shape> Default for YamlSerializer<'shape> {
163    fn default() -> Self {
164        Self::new()
165    }
166}
167
168impl<'shape> Serializer<'shape> for YamlSerializer<'shape> {
169    type Error = YamlSerError;
170
171    fn serialize_u64(&mut self, value: u64) -> Result<(), Self::Error> {
172        let yaml_number = TryInto::<i64>::try_into(value)
173            .map_err(|_| YamlSerError::InvalidNumberToI64Conversion { source_type: "u64" })?;
174        self.write_value(Yaml::Integer(yaml_number))
175    }
176
177    fn serialize_u128(&mut self, value: u128) -> Result<(), Self::Error> {
178        let yaml_number = TryInto::<i64>::try_into(value).map_err(|_| {
179            YamlSerError::InvalidNumberToI64Conversion {
180                source_type: "u128",
181            }
182        })?;
183        self.write_value(Yaml::Integer(yaml_number))
184    }
185
186    fn serialize_i64(&mut self, value: i64) -> Result<(), Self::Error> {
187        self.write_value(Yaml::Integer(value))
188    }
189
190    fn serialize_i128(&mut self, value: i128) -> Result<(), Self::Error> {
191        let yaml_number = TryInto::<i64>::try_into(value).map_err(|_| {
192            YamlSerError::InvalidNumberToI64Conversion {
193                source_type: "i128",
194            }
195        })?;
196        self.write_value(Yaml::Integer(yaml_number))
197    }
198
199    fn serialize_f64(&mut self, value: f64) -> Result<(), Self::Error> {
200        self.write_value(Yaml::Real(value.to_string()))
201    }
202
203    fn serialize_bool(&mut self, value: bool) -> Result<(), Self::Error> {
204        self.write_value(Yaml::Boolean(value))
205    }
206
207    fn serialize_char(&mut self, value: char) -> Result<(), Self::Error> {
208        self.write_value(Yaml::String(value.to_string()))
209    }
210
211    fn serialize_str(&mut self, value: &str) -> Result<(), Self::Error> {
212        self.write_value(Yaml::String(value.to_string()))
213    }
214
215    fn serialize_bytes(&mut self, _value: &[u8]) -> Result<(), Self::Error> {
216        Err(YamlSerError::UnsupportedByteArray)
217    }
218
219    fn serialize_none(&mut self) -> Result<(), Self::Error> {
220        self.write_value(Yaml::Null)
221    }
222
223    fn serialize_unit(&mut self) -> Result<(), Self::Error> {
224        self.write_value(Yaml::Null)
225    }
226
227    fn serialize_unit_variant(
228        &mut self,
229        _variant_index: usize,
230        _variant_name: &'shape str,
231    ) -> Result<(), Self::Error> {
232        todo!()
233    }
234
235    fn start_object(&mut self, _len: Option<usize>) -> Result<(), Self::Error> {
236        self.set_current(Yaml::Hash(Hash::new()));
237
238        Ok(())
239    }
240
241    fn start_array(&mut self, _len: Option<usize>) -> Result<(), Self::Error> {
242        self.set_current(Yaml::Array(Array::new()));
243
244        Ok(())
245    }
246
247    fn start_map(&mut self, _len: Option<usize>) -> Result<(), Self::Error> {
248        self.set_current(Yaml::Hash(Hash::new()));
249
250        Ok(())
251    }
252
253    fn serialize_field_name(&mut self, name: &'shape str) -> Result<(), Self::Error> {
254        self.push_key(Cow::Borrowed(name), "field");
255
256        Ok(())
257    }
258
259    fn begin_map_key(&mut self) -> Result<(), Self::Error> {
260        self.current = KeyOrValue::Key;
261
262        Ok(())
263    }
264
265    fn end_map_key(&mut self) -> Result<(), Self::Error> {
266        self.current = KeyOrValue::Value;
267
268        Ok(())
269    }
270
271    fn end_map_value(&mut self) -> Result<(), Self::Error> {
272        self.pop_key("map item");
273
274        Ok(())
275    }
276
277    fn end_field(&mut self) -> Result<(), Self::Error> {
278        self.pop_key("field");
279
280        Ok(())
281    }
282}
283
284/// What type the current item is.
285#[derive(Clone, Copy, PartialEq, Eq)]
286enum KeyOrValue {
287    /// First part of a map item.
288    Key,
289    /// A regular value, can be a field, array item, etc.
290    Value,
291}
292
293/// Serialize any `Facet` type to a YAML string.
294#[cfg(feature = "alloc")]
295pub fn to_string<'a, T: facet_core::Facet<'a>>(value: &'a T) -> Result<String, YamlSerError> {
296    let mut serializer = YamlSerializer::new();
297    value.serialize(&mut serializer)?;
298
299    Ok(serializer.into_string())
300}
301
302/// Static type name for a YAML type.
303fn type_name(yaml: &Yaml) -> &'static str {
304    match yaml {
305        Yaml::Real(_) => "real",
306        Yaml::Integer(_) => "integer",
307        Yaml::String(_) => "string",
308        Yaml::Boolean(_) => "boolean",
309        Yaml::Array(_) => "array",
310        Yaml::Hash(_) => "hash",
311        Yaml::Alias(_) => "alias",
312        Yaml::Null => "null",
313        Yaml::BadValue => "bad value",
314    }
315}