cjson/
lib.rs

1//! A canonical JSON serializer that tries to be
2//! compliant with [the OLPC minimal specification for canonical JSON][olpc].
3//! Additionally, the implementation also tries to be fully compatible with the [Go
4//! canonical JSON implementation][docker/go/canonical] used across the Docker and
5//! Notary ecosystem.
6//! Example - reading a JSON file and printing its canonical representation:
7//!
8//! ```rust
9//! let data = r#"
10//! {
11//!     "name": "John Doe",
12//!     "age": 43,
13//!     "phones": [
14//!         "+44 1234567",
15//!         "+44 2345678"
16//!     ]
17//! }"#;
18//! let res: serde_json::Value =
19//!     serde_json::from_str(data).expect("cannot deserialize input file");
20//!
21//! let canonical = cjson::to_string(&res).expect("cannot write canonical JSON");
22//! let expected = r#"{"age":43,"name":"John Doe","phones":["+44 1234567","+44 2345678"]}"#;
23//! assert_eq!(canonical, expected);
24//!
25//! ```
26#![deny(missing_docs)]
27
28use serde_json::{self, Value};
29use std::{collections::BTreeMap, io};
30
31#[cfg(test)]
32mod tests;
33
34/// Serialize the given data structure as canonical JSON into the IO stream.
35pub fn to_writer<W, T: Sized>(mut writer: W, value: &T) -> Result<(), Error>
36where
37    W: io::Write,
38    T: serde::ser::Serialize,
39{
40    let v = serde_json::to_value(value)?;
41    let bytes = canonicalize(&v)?;
42    writer.write_all(&bytes)?;
43    Ok(())
44}
45
46/// Serialize the given data structure as a canonical JSON byte vector.
47pub fn to_vec<T: Sized>(value: &T) -> Result<Vec<u8>, Error>
48where
49    T: serde::Serialize,
50{
51    let mut writer = Vec::new();
52    to_writer(&mut writer, value)?;
53    Ok(writer)
54}
55
56/// Serialize the given data structure as a String of canonical JSON.
57pub fn to_string<T: Sized>(value: &T) -> Result<String, Error>
58where
59    T: serde::Serialize,
60{
61    Ok(unsafe { String::from_utf8_unchecked(to_vec(value)?) })
62}
63
64fn canonicalize(val: &serde_json::Value) -> Result<Vec<u8>, Error> {
65    let cv = from_value(val)?;
66    let mut buf = Vec::new();
67    let _ = cv.write(&mut buf);
68    Ok(buf)
69}
70
71enum CanonicalValue {
72    Null,
73    Bool(bool),
74    Number(Number),
75    String(String),
76    Array(Vec<CanonicalValue>),
77    Object(BTreeMap<String, CanonicalValue>),
78}
79
80enum Number {
81    I64(i64),
82}
83
84impl CanonicalValue {
85    fn write(&self, mut buf: &mut Vec<u8>) -> Result<(), Error> {
86        match *self {
87            CanonicalValue::Null => {
88                buf.extend(b"null");
89                Ok(())
90            }
91            CanonicalValue::Bool(true) => {
92                buf.extend(b"true");
93                Ok(())
94            }
95            CanonicalValue::Bool(false) => {
96                buf.extend(b"false");
97                Ok(())
98            }
99            CanonicalValue::Number(Number::I64(n)) => match itoa::write(buf, n) {
100                Ok(_) => Ok(()),
101                Err(err) => Err(Error::Custom(format!("cannot write number: {}", err))),
102            },
103            CanonicalValue::String(ref s) => {
104                let s = serde_json::to_string(&Value::String(s.clone()))?;
105                buf.extend(s.as_bytes());
106                Ok(())
107            }
108            CanonicalValue::Array(ref arr) => {
109                buf.push(b'[');
110                let mut first = true;
111                for a in arr.iter() {
112                    if !first {
113                        buf.push(b',');
114                    }
115                    a.write(&mut buf)?;
116                    first = false;
117                }
118                buf.push(b']');
119                Ok(())
120            }
121            CanonicalValue::Object(ref obj) => {
122                buf.push(b'{');
123                let mut first = true;
124                for (k, v) in obj.iter() {
125                    if !first {
126                        buf.push(b',');
127                    }
128                    first = false;
129                    let k = serde_json::to_string(&Value::String(k.clone()))?;
130                    buf.extend(k.as_bytes());
131                    buf.push(b':');
132                    v.write(&mut buf)?;
133                }
134                buf.push(b'}');
135                Ok(())
136            }
137        }
138    }
139}
140
141fn from_value(val: &Value) -> Result<CanonicalValue, Error> {
142    match *val {
143        Value::Null => Ok(CanonicalValue::Null),
144        Value::Bool(b) => Ok(CanonicalValue::Bool(b)),
145        Value::Number(ref n) => {
146            let x = n.as_i64();
147            match x {
148                Some(x) => Ok(CanonicalValue::Number(Number::I64(x))),
149                None => Err(Error::Custom(String::from(format!(
150                    "unsupported value in canonical JSON: {}",
151                    n
152                )))),
153            }
154        }
155        Value::String(ref s) => Ok(CanonicalValue::String(s.clone())),
156        Value::Array(ref arr) => {
157            let mut out = Vec::new();
158            for res in arr.iter().map(|v| from_value(v)) {
159                out.push(res?)
160            }
161            Ok(CanonicalValue::Array(out))
162        }
163        Value::Object(ref obj) => {
164            let mut out = BTreeMap::new();
165            for (k, v) in obj.iter() {
166                let _ = out.insert(k.clone(), from_value(v)?);
167            }
168            Ok(CanonicalValue::Object(out))
169        }
170    }
171}
172
173/// This enum represents all errors that can be returned when
174/// trying to serialize something as canonical JSON.
175#[derive(Debug)]
176pub enum Error {
177    /// Custom generic error.
178    Custom(String),
179    /// IO error.
180    Io(io::Error),
181}
182
183impl From<serde_json::Error> for Error {
184    fn from(err: serde_json::Error) -> Error {
185        use serde_json::error::Category;
186        match err.classify() {
187            Category::Io => Error::Io(err.into()),
188            Category::Syntax | Category::Data | Category::Eof => {
189                Error::Custom(String::from(format!("{}", err)))
190            }
191        }
192    }
193}
194
195impl From<io::Error> for Error {
196    fn from(error: io::Error) -> Error {
197        Error::Io(error)
198    }
199}