blot/
json.rs

1// Copyright 2018 Arnau Siches
2
3// Licensed under the MIT license <LICENSE or http://opensource.org/licenses/MIT>.
4// This file may not be copied, modified, or distributed except
5// according to those terms.
6
7//! Blot implementation for common JSON.
8//!
9//! This implementation treats all numbers as f64.
10//!
11//! ```
12//! extern crate serde_json;
13//! extern crate blot;
14//! use serde_json::{self, Value};
15//! use blot::core::Blot;
16//! use blot::multihash::Sha2256;
17//!
18//! let data = r#"["foo", "bar"]"#;
19//! let value: Value = serde_json::from_str(data).unwrap();
20//!
21//! assert_eq!(format!("{}", &value.digest(Sha2256)), "122032ae896c413cfdc79eec68be9139c86ded8b279238467c216cf2bec4d5f1e4a2");
22//! ```
23
24use core::Blot;
25use multihash::{Harvest, Multihash};
26use serde_json::{Map, Number, Value};
27use tag::Tag;
28
29impl Blot for Map<String, Value> {
30    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
31        let mut list: Vec<Vec<u8>> = self
32            .iter()
33            .map(|(k, v)| {
34                let mut res: Vec<u8> = Vec::with_capacity(64);
35                res.extend_from_slice(k.blot(digester).as_slice());
36                res.extend_from_slice(v.blot(digester).as_slice());
37
38                res
39            }).collect();
40
41        list.sort_unstable();
42
43        digester.digest_collection(Tag::Dict, list)
44    }
45}
46
47#[cfg(feature = "common_json")]
48impl Blot for Number {
49    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
50        self.as_f64()
51            .expect("Casting JSON Number as f64 failed")
52            .blot(digester)
53    }
54}
55
56#[cfg(not(feature = "common_json"))]
57impl Blot for Number {
58    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
59        if self.is_f64() {
60            self.as_f64()
61                .expect("Casting JSON Number as f64 failed")
62                .blot(digester)
63        } else if self.is_u64() {
64            self.as_u64()
65                .expect("Casting JSON Number as u64 failed")
66                .blot(digester)
67        } else {
68            self.as_i64()
69                .expect("Casting JSON Number as i64 failed")
70                .blot(digester)
71        }
72    }
73}
74
75impl Blot for Value {
76    fn blot<D: Multihash>(&self, digester: &D) -> Harvest {
77        use hex::FromHex;
78        match self {
79            Value::Null => None::<u8>.blot(digester),
80            Value::Bool(raw) => raw.blot(digester),
81            Value::Number(raw) => raw.blot(digester),
82            Value::String(raw) => {
83                // TODO: Consider moving to Seal
84                if raw.starts_with("**REDACTED**") {
85                    let slice =
86                        Vec::from_hex(raw.get(12..).expect("REDACTED")).expect("Hexadecimal");
87
88                    slice.into_boxed_slice().into()
89                } else {
90                    raw.blot(digester)
91                }
92            }
93            Value::Array(raw) => raw.blot(digester),
94            Value::Object(raw) => raw.blot(digester),
95        }
96    }
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use multihash::Sha2256;
103    use serde_json::{self, Value};
104
105    #[test]
106    fn common() {
107        let expected = "122032ae896c413cfdc79eec68be9139c86ded8b279238467c216cf2bec4d5f1e4a2";
108        let value: Value = serde_json::from_str(r#"["foo", "bar"]"#).unwrap();
109        let actual = format!("{}", &value.digest(Sha2256));
110
111        assert_eq!(actual, expected);
112    }
113
114    #[test]
115    fn common_redacted() {
116        let expected = "122032ae896c413cfdc79eec68be9139c86ded8b279238467c216cf2bec4d5f1e4a2";
117        let value: Value = serde_json::from_str(r#"["**REDACTED**a6a6e5e783c363cd95693ec189c2682315d956869397738679b56305f2095038", "bar"]"#).unwrap();
118        let actual = format!("{}", &value.digest(Sha2256));
119
120        assert_eq!(actual, expected);
121    }
122
123    #[cfg(not(feature = "common_json"))]
124    mod default {
125        use super::*;
126        use multihash::Sha2256;
127        use serde_json::{self, Value};
128
129        #[test]
130        fn int_list() {
131            let pairs = [
132                (
133                    r#"[123]"#,
134                    "12201b93f704451e1a7a1b8c03626ffcd6dec0bc7ace947ff60d52e1b69b4658ccaa",
135                ),
136                (
137                    r#"[1, 2, 3]"#,
138                    "1220157bf16c70bd4c9673ffb5030552df0ee2c40282042ccdf6167850edc9044ab7",
139                ),
140                (
141                    r#"[123456789012345]"#,
142                    "12203488b9bc37cce8223a032760a9d4ef488cdfebddd9e1af0b31fcd1d7006369a4",
143                ),
144                (
145                    r#"[123456789012345, 678901234567890]"#,
146                    "1220031ef1aaeccea3bced3a1c6237a4fc00ed4d629c9511922c5a3f4e5c128b0ae4",
147                ),
148            ];
149            for (raw, expected) in pairs.iter() {
150                let value: Value = serde_json::from_str(raw).unwrap();
151                let actual = format!("{}", &value.digest(Sha2256));
152
153                assert_eq!(&actual, expected);
154            }
155        }
156
157        #[test]
158        fn int_float_mix() {
159            let pairs = [
160                (
161                    r#"["foo", {"bar":["baz", null, 1.0, 1.5, 0.0001, 1000.0, 2.0, -23.1234, 2.0]}]"#,
162                        "1220783a423b094307bcb28d005bc2f026ff44204442ef3513585e7e73b66e3c2213"
163                ),
164                (
165                    r#"["foo", {"bar":["baz", null, 1, 1.5, 0.0001, 1000, 2, -23.1234, 2]}]"#,
166                    "1220726e7ae9e3fadf8a2228bf33e505a63df8db1638fa4f21429673d387dbd1c52a"
167                )
168            ];
169            for (raw, expected) in pairs.iter() {
170                let value: Value = serde_json::from_str(raw).unwrap();
171                let actual = format!("{}", &value.digest(Sha2256));
172
173                assert_eq!(&actual, expected);
174            }
175        }
176
177        #[test]
178        fn list() {
179            let expected = "1220acac86c0e609ca906f632b0e2dacccb2b77d22b0621f20ebece1a4835b93f6f0";
180            let value: Value = serde_json::from_str(r#"[]"#).unwrap();
181            let actual = format!("{}", &value.digest(Sha2256));
182
183            assert_eq!(&actual, expected);
184        }
185    }
186
187    #[cfg(feature = "common_json")]
188    mod common_json {
189        use super::*;
190        use multihash::Sha2256;
191        use serde_json::{self, Value};
192
193        #[test]
194        fn int_list() {
195            let pairs = [
196                (
197                    r#"[123]"#,
198                    "12202e72db006266ed9cdaa353aa22b9213e8a3c69c838349437c06896b1b34cee36",
199                ),
200                (
201                    r#"[1, 2, 3]"#,
202                    "1220925d474ac71f6e8cb35dd951d123944f7cabc5cda9a043cf38cd638cc0158db0",
203                ),
204                (
205                    r#"[123456789012345]"#,
206                    "1220f446de5475e2f24c0a2b0cd87350927f0a2870d1bb9cbaa794e789806e4c0836",
207                ),
208                (
209                    r#"[123456789012345, 678901234567890]"#,
210                    "1220d4cca471f1c68f62fbc815b88effa7e52e79d110419a7c64c1ebb107b07f7f56",
211                ),
212            ];
213            for (raw, expected) in pairs.iter() {
214                let value: Value = serde_json::from_str(raw).unwrap();
215                let actual = format!("{}", &value.digest(Sha2256));
216
217                assert_eq!(&actual, expected);
218            }
219        }
220    }
221}