compacto/
decompress.rs

1use serde_json::{Map, Value};
2
3use crate::{error::Error, Result};
4
5#[derive(Debug)]
6pub struct Decompressor {
7    refs: Vec<Value>,
8    input: Value,
9    output: Value,
10}
11
12impl Decompressor {
13    /// Construct a new Decompressor using a a JSON string and default values
14    ///
15    /// Example
16    /// ```
17    /// let mut compacto = compacto::Decompressor::new(r#"[{"0":1,"1": 0},["a","b"]]"#).unwrap();
18    /// println!("{:?}", compacto.decompress().unwrap());
19    /// ```
20    pub fn new(value: &str) -> Result<Self> {
21        let input = serde_json::from_str(value)?;
22
23        Ok(Self {
24            input,
25            refs: Vec::new(),
26            output: Value::default(),
27        })
28    }
29
30    /// Decompress JSON
31    ///
32    /// decompress will first check if the input is an array and its length is equal to two.  
33    /// If it does not match the "criteria to decompress," the original input will be returned.
34    /// If the input matches the criteria, we rebuilt the JSON using the reference table.
35    pub fn decompress(&mut self) -> Result<String> {
36        if !self.input.is_array() || self.input.as_array().unwrap().len() != 2 {
37            return Ok(self.input.to_string());
38        }
39
40        self.refs = match self.input.get(1) {
41            Some(values) => values.as_array().unwrap().clone(),
42            None => Vec::new(),
43        };
44
45        let result = match self.input.get(0) {
46            Some(value) => {
47                let output = match value {
48                    Value::Array(a) => self.find_array_value_by_ref(a),
49                    Value::Object(o) => self.find_object_value_by_ref(o),
50                    _ => self.find_value_by_ref(value),
51                }?;
52
53                output.to_string()
54            }
55            None => Value::Null.to_string(),
56        };
57
58        Ok(result)
59    }
60
61    pub(self) fn find_array_value_by_ref(&self, value: &[Value]) -> Result<Value> {
62        let items: Result<Vec<Value>> = value
63            .iter()
64            .map(|v| match v {
65                Value::Array(a) => self.find_array_value_by_ref(a),
66                Value::Object(o) => self.find_object_value_by_ref(o),
67                _ => self.find_value_by_ref(v),
68            })
69            .collect();
70
71        Ok(Value::Array(items?))
72    }
73
74    pub(self) fn find_object_value_by_ref(&self, value: &Map<String, Value>) -> Result<Value> {
75        let mut new_obj: Map<String, Value> = Map::new();
76
77        for (obj_key, obj_value) in value.clone().iter() {
78            let k = self.refs[obj_key.parse::<usize>().unwrap()]
79                .as_str()
80                .unwrap();
81
82            let v = match obj_value {
83                Value::Array(a) => self.find_array_value_by_ref(a),
84                Value::Object(o) => self.find_object_value_by_ref(o),
85                _ => self.find_value_by_ref(obj_value),
86            }?;
87
88            new_obj.insert(k.to_string(), v);
89        }
90
91        Ok(Value::Object(new_obj))
92    }
93
94    pub(self) fn find_value_by_ref(&self, value: &Value) -> Result<Value> {
95        match value {
96            Value::Number(n) => {
97                let index = n.as_u64().unwrap() as usize;
98                Ok(self.refs[index].clone())
99            }
100            Value::String(s) => {
101                let index: usize = s.parse().unwrap();
102                Ok(self.refs[index].clone())
103            }
104            _ => Err(Error::UnknownJSONValueRef(value.to_string())),
105        }
106    }
107}
108
109/// Decompress JSON from a string
110///
111/// # Example
112///
113/// ```
114/// let json = r#"[[{"0":1,"2":3},{"0":4,"2":3}],["id","123","name","Eduardo","456"]]"#;
115/// let output = compacto::decompress_json(json).unwrap();
116/// println!("{:#?}", output);
117/// ```
118pub fn decompress_json(json: &str) -> Result<String> {
119    Decompressor::new(json)?.decompress()
120}
121
122#[cfg(test)]
123mod test {
124    use super::*;
125
126    const OUTPUT_SAMPLE: &str = include_str!("../../samples/test-samples/output.json");
127    const INPUT_SAMPLE: &str = include_str!("../../samples/test-samples/input.json");
128
129    #[test]
130    fn should_return_valid_json() -> crate::Result<()> {
131        let result = Decompressor::new(OUTPUT_SAMPLE)?.decompress()?;
132        let output: Value = serde_json::from_str(&result)?;
133        assert_eq!(true, output.is_object());
134        Ok(())
135    }
136
137    #[test]
138    fn should_be_equal_to_original_json() -> crate::Result<()> {
139        let result = Decompressor::new(OUTPUT_SAMPLE)?.decompress()?;
140        let result_value: Value = serde_json::from_str(&result)?;
141        let expect: Value = serde_json::from_str(INPUT_SAMPLE)?;
142        assert_eq!(expect, result_value);
143        Ok(())
144    }
145
146    #[test]
147    fn should_return_original_input_if_not_array() -> crate::Result<()> {
148        let result = Decompressor::new(r#"{"ok":"ok"}"#)?.decompress()?;
149        let result_value: Value = serde_json::from_str(&result)?;
150        let mut expected = Map::new();
151        expected.insert("ok".to_string(), Value::String("ok".to_string()));
152        assert_eq!(Value::Object(expected), result_value);
153        Ok(())
154    }
155
156    #[test]
157    fn should_return_original_input_if_not_array_expected_length() -> crate::Result<()> {
158        let result = Decompressor::new(r#"[{"ok":"ok"}]"#)?.decompress()?;
159        let result_obj: Value = serde_json::from_str(&result)?;
160        let mut expected = Map::new();
161        expected.insert("ok".to_string(), Value::String("ok".to_string()));
162        assert_eq!(Value::Array(vec![Value::Object(expected)]), result_obj);
163        Ok(())
164    }
165}