serde_value_utils/
flatten.rs

1// Copyright 2019-present, OVH SAS
2// All rights reserved.
3//
4// This OVH Software is licensed to you under the MIT license <LICENSE-MIT
5// https://opensource.org/licenses/MIT> or the Modified BSD license <LICENSE-BSD
6// https://opensource.org/licenses/BSD-3-Clause>, at your option. This file may not be copied,
7// modified, or distributed except according to those terms. Please review the Licences for the
8// specific language governing permissions and limitations relating to use of the SAFE Network
9// Software.
10
11use std::collections::BTreeMap;
12
13use serde_value::Value;
14
15/// Serializer use to convert a struct which implement `Serialize` into a flatten `MapTree` ( which
16/// means a dict with key/value of only one depth)
17struct FlatSerializer {
18    /// Key separator used to represent depths
19    /// E.g: {"a": {"b": 5}} => {"a_b": 5} with key_separator="_"
20    key_separator: String,
21    /// Prefix to use on the first key level
22    /// E.g: {"a": {"b": 5}} => {"_a_b": 5} with key_separator="_" and prefix="_"
23    prefix: String,
24}
25
26impl FlatSerializer {
27    /// Initialize the struct using the given configuration values
28    pub fn new(key_separator: String, prefix: String) -> FlatSerializer {
29        FlatSerializer { key_separator, prefix }
30    }
31    /// "Normal" key formatting
32    #[cfg(not(feature = "with-schema"))]
33    fn format_key(&self, xpath: &str, key: &str, _value: &Value) -> String {
34        match (xpath, key) {
35            (_, "") => String::new(),
36            ("", k) => format!("{}{}", self.prefix, k),
37            (x, k) => format!("{}{}{}", x, self.key_separator, k)
38        }
39    }
40    /// Suffix the key name with the type of value
41    /// * `bool` => `bool`
42    /// * `u*` => `double`
43    /// * `i*` => `long`
44    /// * `f*` => `float`
45    #[cfg(feature = "with-schema")]
46    fn _schema_suffix(&self, value: &Value) -> String {
47        match *value {
48            Value::Bool(_) => format!("{}bool", self.key_separator),
49            Value::U8(_) | Value::U16(_) | Value::U32(_) | Value::U64(_) => format!("{}double", self.key_separator),
50            Value::I8(_) | Value::I16(_) | Value::I32(_) | Value::I64(_) => format!("{}long", self.key_separator),
51            Value::F32(_) | Value::F64(_) => format!("{}float", self.key_separator),
52            _ => "".into()
53        }
54    }
55    /// Will suffix the key name with the schema
56    #[cfg(feature = "with-schema")]
57    fn format_key(&self, xpath: &str, key: &str, value: &Value) -> String {
58        match (xpath, key) {
59            (_, "") => String::new(),
60            ("", k) => format!("{}{}{}", self.prefix, k, self._schema_suffix(value)),
61            (x, k) => format!("{}{}{}{}", x, self.key_separator, k, self._schema_suffix(value)),
62        }
63    }
64    /// Dissemble the struct attribute into a flatten key/ value pair.
65    pub fn disassemble(&self, xpath: &str, key: &str, value: &Value) -> BTreeMap<Value, Value> {
66        let mut parts = BTreeMap::new();
67        match value {
68            Value::Map(ref tree) => {
69                for (k, v) in tree.iter() {
70                    let subkey = match k {
71                        Value::String(data) => format!("{}", data),
72                        Value::Char(data) => format!("{}", data),
73                        _ => panic!("Map keys MUST be strings or char")
74                    };
75                    parts.append(&mut self.disassemble(&self.format_key(xpath, &key, value), &subkey, v));
76                };
77            }
78            Value::Seq(ref values) => {
79                for (i, val) in values.iter().enumerate() {
80                    parts.append(&mut self.disassemble(&mut self.format_key(xpath, key, value), &format!("{}", i), val));
81                }
82            }
83            _ => {
84                parts.insert(Value::String(self.format_key(xpath, key, value)), value.clone());
85            }
86        };
87        parts
88    }
89}
90
91/// Flatten any struct which implement `Serialize` into a `BTreeMap<serde_value::Value, serde_value::Value>`
92/// with only one depth.
93///
94/// ```rust
95/// #[macro_use]
96/// extern crate serde_derive;
97/// extern crate serde_json;
98/// extern crate serde_value_utils;
99///
100/// #[derive(Serialize, Clone, Debug)]
101/// struct SubFoo {
102///     a: String,
103///     b: u64,
104/// }
105///
106/// #[derive(Serialize, Clone, Debug)]
107/// struct Foo {
108///     a: String,
109///     b: f64,
110///     c: Vec<i8>,
111///     d: SubFoo,
112/// }
113///
114/// fn main() {
115///     let foo = Foo { a: "test".into(), b: 0.5, c: vec![5, 9], d: SubFoo { a: "subtest".into(), b: 695217 } };
116///     let ser = serde_value_utils::to_flatten_maptree("_", Some("_"), &foo).unwrap();
117///     println!("{}", serde_json::to_string_pretty(&ser).unwrap());
118/// }
119/// ```
120/// **Output**:
121/// ```json
122///  {
123///   "_a": "test",
124///   "_b": 0.5,
125///   "_c_0": 5,
126///   "_c_1": 9,
127///   "_d_a": "subtest",
128///   "_d_b": 695217
129/// }
130/// ```
131pub fn to_flatten_maptree<T: ?Sized>(key_separator: &str, prefix: Option<&str>, src: &T) -> Result<BTreeMap<serde_value::Value, serde_value::Value>, serde_value::SerializerError>
132    where T: serde::Serialize {
133    Ok(FlatSerializer::new(key_separator.into(), prefix.unwrap_or("").into())
134        .disassemble("", "", &serde_value::to_value(src)?))
135}
136