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