Skip to main content

json_streaming/blocking/
object.rs

1use crate::blocking::array::JsonArray;
2use crate::blocking::io::BlockingWrite;
3use crate::blocking::json_writer::JsonWriter;
4use crate::shared::*;
5
6/// A [JsonObject] is the API for writing a JSON object, i.e. a sequence of key/value pairs. The
7///  closing `}` is written when the [JsonObject] instance goes out of scope, or when its `end()`
8///  function is called.
9///
10/// For nested objects or arrays, the function calls return new [JsonObject] or [JsonArray] instances,
11///  respectively. Rust's type system ensures that applications can only interact with the innermost
12///  such instance, and call outer instances only when all nested instances have gone out of scope.
13///
14/// A typical use of the library is to create a [JsonWriter] and then wrap it in a top-level
15///  [JsonObject] instance.
16pub struct JsonObject<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> {
17    writer: &'a mut JsonWriter<'b, W, F, FF>,
18    is_initial: bool,
19    is_ended: bool,
20}
21impl<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> JsonObject<'a, 'b, W, F, FF> {
22    /// Create a new [JsonObject] instance. Application code can do this explicitly only initially
23    ///  as a starting point for writing JSON. Nested objects are created by the library.
24    pub fn new(writer: &'a mut JsonWriter<'b, W, F, FF>) -> Result<Self, W::Error> {
25        writer.write_bytes(b"{")?;
26        writer.write_format_after_start_nested()?;
27        Ok(JsonObject {
28            writer,
29            is_initial: true,
30            is_ended: false,
31        })
32    }
33
34    fn write_key(&mut self, key: &str) -> Result<(), W::Error> {
35        if !self.is_initial {
36            self.writer.write_bytes(b",")?;
37            self.writer.write_format_after_element()?;
38        }
39        self.is_initial = false;
40        self.writer.write_format_indent()?;
41        self.writer.write_escaped_string(key)?;
42        self.writer.write_bytes(b":")?;
43        self.writer.write_format_after_key()
44    }
45
46    /// Write a pre-serialized JSON snippet as a value with a given key.
47    ///
48    /// NB: This method does *not* perform any checks on the value, assuming it to be a valid JSON
49    ///  value.
50    /// NB: This method does not apply formatting rules to the JSON snippet, but writes it 'as-is'.
51    pub fn unchecked_write_raw_value(&mut self, key: &str, value_json: &str) -> Result<(), W::Error> {
52        self.write_key(key)?;
53        self.writer.write_bytes(value_json.as_bytes())
54    }
55
56    /// Write a key/value pair with element type 'string', escaping the provided string value.
57    pub fn write_string_value(&mut self, key: &str, value: &str) -> Result<(), W::Error> {
58        self.write_key(key)?;
59        self.writer.write_escaped_string(value)
60    }
61
62    /// Write a key/value pair with element type 'bool'
63    pub fn write_bool_value(&mut self, key: &str, value: bool) -> Result<(), W::Error> {
64        self.write_key(key)?;
65        self.writer.write_bool(value)
66    }
67
68    /// Write a key with a null literal as its value
69    pub fn write_null_value(&mut self, key: &str) -> Result<(), W::Error> {
70        self.write_key(key)?;
71        self.writer.write_bytes(b"null")
72    }
73
74    /// Write a key/value pair with an f64 value. If the value is not finite (i.e. infinite or NaN),
75    ///  a null literal is written instead. Different behavior (e.g. leaving out the whole key/value
76    ///  pair for non-finite numbers, representing them in some other way etc.) is the responsibility
77    ///  of application code.
78    pub fn write_f64_value(&mut self, key: &str, value: f64) -> Result<(), W::Error> {
79        self.write_key(key)?;
80        self.writer.write_f64(value)
81    }
82
83    /// Write a key/value pair with an f32 value. If the value is not finite (i.e. infinite or NaN),
84    ///  a null literal is written instead. Different behavior (e.g. leaving out the whole key/value
85    ///  pair for non-finite numbers, representing them in some other way etc.) is the responsibility
86    ///  of application code.
87    pub fn write_f32_value(&mut self, key: &str, value: f32) -> Result<(), W::Error> {
88        self.write_key(key)?;
89        self.writer.write_f32(value)
90    }
91
92    /// Start a nested object under a given key. This function returns a new [JsonObject] instance
93    ///  for writing elements to the nested object. When the returned [JsonObject] goes out of scope
94    ///  (per syntactic scope or an explicit call to `end()`), the nested object is closed, and
95    ///  application code can continue adding elements to the owning `self` object.
96    pub fn start_object<'c, 'x>(&'x mut self, key: &str) -> Result<JsonObject<'c, 'b, W, F, FF>, W::Error>
97    where 'a: 'c, 'x: 'c
98    {
99        self.write_key(key)?;
100        JsonObject::new(self.writer)
101    }
102
103    /// Start a nested array under a given key. This function returns a new [JsonArray] instance
104    ///  for writing elements to the nested object. When the returned [JsonArray] goes out of scope
105    ///  (per syntactic scope or an explicit call to `end()`), the nested array is closed, and
106    ///  application code can continue adding elements to the owning `self` object.
107    pub fn start_array<'c, 'x>(&'x mut self, key: &str) -> Result<JsonArray<'c, 'b, W, F, FF>, W::Error>
108    where 'a: 'c, 'x: 'c
109    {
110        self.write_key(key)?;
111        JsonArray::new(self.writer)
112    }
113
114    /// Explicitly end this object's lifetime and write the closing bracket.
115    pub fn end(self) -> Result<(), W::Error> {
116        let mut mut_self = self;
117        mut_self._end()
118    }
119
120    fn _end(&mut self) -> Result<(), W::Error> {
121        self.writer.write_format_before_end_nested(self.is_initial)?;
122        self.writer.write_bytes(b"}")?;
123        self.is_ended = true;
124        Ok(())
125    }
126}
127
128macro_rules! write_obj_int {
129    ($t:ty ; $f:ident) => {
130impl<'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> JsonObject<'a, 'b, W, F, FF> {
131    /// Write a key/value pair with an int value of type $t.
132    pub fn $f(&mut self, key: &str, value: $t) -> Result<(), W::Error> {
133        self.write_key(key)?;
134        self.writer.write_raw_num(value)
135    }
136}
137    };
138}
139write_obj_int!(i8; write_i8_value);
140write_obj_int!(u8; write_u8_value);
141write_obj_int!(i16; write_i16_value);
142write_obj_int!(u16; write_u16_value);
143write_obj_int!(i32; write_i32_value);
144write_obj_int!(u32; write_u32_value);
145write_obj_int!(i64; write_i64_value);
146write_obj_int!(u64; write_u64_value);
147write_obj_int!(i128; write_i128_value);
148write_obj_int!(u128; write_u128_value);
149write_obj_int!(isize; write_isize_value);
150write_obj_int!(usize; write_usize_value);
151
152impl <'a, 'b, W: BlockingWrite, F: JsonFormatter, FF: FloatFormat> Drop for JsonObject<'a, 'b, W, F, FF> {
153    fn drop(&mut self) {
154        if !self.is_ended {
155            if let Err(e) = self._end() {
156                self.writer.set_unreported_error(e);
157            }
158        }
159    }
160}
161
162
163#[cfg(test)]
164mod tests {
165    use super::*;
166    use rstest::*;
167    use std::io;
168
169    type OS<'a, 'b> = JsonObject<'a, 'b, Vec<u8>, CompactFormatter, DefaultFloatFormat>;
170
171    #[rstest]
172    #[case::empty(Box::new(|_ser: &mut OS| Ok(())), "{}")]
173    #[case::single(Box::new(|ser: &mut OS| ser.write_null_value("a")), r#"{"a":null}"#)]
174    #[case::two(Box::new(|ser: &mut OS| { ser.write_u32_value("a", 1)?; ser.write_u32_value("b", 2) }), r#"{"a":1,"b":2}"#)]
175    #[case::nested_arr(Box::new(|ser: &mut OS| { ser.start_array("x")?.end() }), r#"{"x":[]}"#)]
176    #[case::nested_arr_with_el(Box::new(|ser: &mut OS| { let mut n = ser.start_array("y")?; n.write_u32_value(5) }), r#"{"y":[5]}"#)]
177    #[case::nested_arr_first(Box::new(|ser: &mut OS| { ser.start_array("z")?.end()?; ser.write_u32_value("q", 4) }), r#"{"z":[],"q":4}"#)]
178    #[case::nested_arr_last(Box::new(|ser: &mut OS| { ser.write_u32_value("q", 6)?; ser.start_array("z")?.end() }), r#"{"q":6,"z":[]}"#)]
179    #[case::nested_arr_between(Box::new(|ser: &mut OS| { ser.write_u32_value("a", 7)?; ser.start_array("b")?.end()?; ser.write_u32_value("c", 9) }), r#"{"a":7,"b":[],"c":9}"#)]
180    #[case::two_nested_arrays(Box::new(|ser: &mut OS| { ser.start_array("d")?.end()?; ser.start_array("e")?.end() }), r#"{"d":[],"e":[]}"#)]
181    #[case::nested_obj(Box::new(|ser: &mut OS| { ser.start_object("f")?.end() }), r#"{"f":{}}"#)]
182    #[case::nested_obj_with_el(Box::new(|ser: &mut OS| { let mut n=ser.start_object("g")?; n.write_u32_value("a", 3) }), r#"{"g":{"a":3}}"#)]
183    #[case::nested_obj_first(Box::new(|ser: &mut OS| { ser.start_object("h")?.end()?; ser.write_u32_value("i", 0) }), r#"{"h":{},"i":0}"#)]
184    #[case::nested_obj_last(Box::new(|ser: &mut OS| { ser.write_u32_value("j", 2)?; ser.start_object("k")?.end() }), r#"{"j":2,"k":{}}"#)]
185    #[case::two_nested_objects(Box::new(|ser: &mut OS| { ser.start_object("l")?.end()?; ser.start_object("m")?.end() }), r#"{"l":{},"m":{}}"#)]
186    fn test_object(#[case] code: Box<dyn Fn(&mut OS) -> io::Result<()>>, #[case] expected: &str) -> io::Result<()> {
187        let mut buf = Vec::new();
188        let mut writer = JsonWriter::new_compact(&mut buf);
189        {
190            let mut object_ser = JsonObject::new(&mut writer)?;
191            code(&mut object_ser)?;
192        }
193
194        let actual = String::from_utf8(buf).unwrap();
195        assert_eq!(actual, expected);
196        Ok(())
197    }
198
199    #[rstest]
200    #[case::null(Box::new(|w: &mut OS| w.write_null_value("a")), "null")]
201    #[case::bool_true(Box::new(|w: &mut OS| w.write_bool_value("a", true)), "true")]
202    #[case::bool_false(Box::new(|w: &mut OS| w.write_bool_value("a", false)), "false")]
203    #[case::raw(Box::new(|w: &mut OS| w.unchecked_write_raw_value("a", "x y z")), "x y z")]
204    #[case::string(Box::new(|w: &mut OS| w.write_string_value("a", "asdf")), r#""asdf""#)]
205    #[case::string_escaped(Box::new(|w: &mut OS| w.write_string_value("a", "\r\n")), r#""\r\n""#)]
206    #[case::u8(Box::new(|w: &mut OS| w.write_u8_value("a", 2u8)), "2")]
207    #[case::i8(Box::new(|w: &mut OS| w.write_i8_value("a", -3i8)), "-3")]
208    #[case::u16(Box::new(|w: &mut OS| w.write_u16_value("a", 4u16)), "4")]
209    #[case::i16(Box::new(|w: &mut OS| w.write_i16_value("a", -5i16)), "-5")]
210    #[case::u32(Box::new(|w: &mut OS| w.write_u32_value("a", 6u32)), "6")]
211    #[case::i32(Box::new(|w: &mut OS| w.write_i32_value("a", -7i32)), "-7")]
212    #[case::u64(Box::new(|w: &mut OS| w.write_u64_value("a", 8u64)), "8")]
213    #[case::i64(Box::new(|w: &mut OS| w.write_i64_value("a", -9i64)), "-9")]
214    #[case::u128(Box::new(|w: &mut OS| w.write_u128_value("a", 12u128)), "12")]
215    #[case::i128(Box::new(|w: &mut OS| w.write_i128_value("a", -13i128)), "-13")]
216    #[case::usize(Box::new(|w: &mut OS| w.write_usize_value("a", 10usize)), "10")]
217    #[case::isize(Box::new(|w: &mut OS| w.write_isize_value("a", -11isize)), "-11")]
218    #[case::f64(Box::new(|w: &mut OS| w.write_f64_value("a", 2.0)), "2")]
219    #[case::f64_exp_5(Box::new(|w: &mut OS| w.write_f64_value("a", 1.234e5)), "123400")]
220    #[case::f64_exp_10(Box::new(|w: &mut OS| w.write_f64_value("a", 1.234e10)), "1.234e10")]
221    #[case::f64_exp_20(Box::new(|w: &mut OS| w.write_f64_value("a", 1.234e20)), "1.234e20")]
222    #[case::f64_exp_neg_3(Box::new(|w: &mut OS| w.write_f64_value("a", 1.234e-3)), "0.001234")]
223    #[case::f64_exp_neg_10(Box::new(|w: &mut OS| w.write_f64_value("a", 1.234e-10)), "1.234e-10")]
224    #[case::f64_neg(Box::new(|w: &mut OS| w.write_f64_value("a", -2.0)), "-2")]
225    #[case::f64_neg_exp_5(Box::new(|w: &mut OS| w.write_f64_value("a", -1.234e5)), "-123400")]
226    #[case::f64_neg_exp_10(Box::new(|w: &mut OS| w.write_f64_value("a", -1.234e10)), "-1.234e10")]
227    #[case::f64_neg_exp_20(Box::new(|w: &mut OS| w.write_f64_value("a", -1.234e20)), "-1.234e20")]
228    #[case::f64_neg_exp_neg_3(Box::new(|w: &mut OS| w.write_f64_value("a", -1.234e-3)), "-0.001234")]
229    #[case::f64_neg_exp_neg_10(Box::new(|w: &mut OS| w.write_f64_value("a", -1.234e-10)), "-1.234e-10")]
230    #[case::f64_inf(Box::new(|w: &mut OS| w.write_f64_value("a", f64::INFINITY)), "null")]
231    #[case::f64_neg_inf(Box::new(|w: &mut OS| w.write_f64_value("a", f64::NEG_INFINITY)), "null")]
232    #[case::f64_nan(Box::new(|w: &mut OS| w.write_f64_value("a", f64::NAN)), "null")]
233    #[case::f32(Box::new(|w: &mut OS| w.write_f32_value("a", 2.0)), "2")]
234    #[case::f32_exp_5(Box::new(|w: &mut OS| w.write_f32_value("a", 1.234e5)), "123400")]
235    #[case::f32_exp_10(Box::new(|w: &mut OS| w.write_f32_value("a", 1.234e10)), "1.234e10")]
236    #[case::f32_exp_20(Box::new(|w: &mut OS| w.write_f32_value("a", 1.234e20)), "1.234e20")]
237    #[case::f32_exp_neg_3(Box::new(|w: &mut OS| w.write_f32_value("a", 1.234e-3)), "0.001234")]
238    #[case::f32_exp_neg_10(Box::new(|w: &mut OS| w.write_f32_value("a", 1.234e-10)), "1.234e-10")]
239    #[case::f32_neg(Box::new(|w: &mut OS| w.write_f32_value("a", -2.0)), "-2")]
240    #[case::f32_neg_exp_5(Box::new(|w: &mut OS| w.write_f32_value("a", -1.234e5)), "-123400")]
241    #[case::f32_neg_exp_10(Box::new(|w: &mut OS| w.write_f32_value("a", -1.234e10)), "-1.234e10")]
242    #[case::f32_neg_exp_20(Box::new(|w: &mut OS| w.write_f32_value("a", -1.234e20)), "-1.234e20")]
243    #[case::f32_neg_exp_neg_3(Box::new(|w: &mut OS| w.write_f32_value("a", -1.234e-3)), "-0.001234")]
244    #[case::f32_neg_exp_neg_10(Box::new(|w: &mut OS| w.write_f32_value("a", -1.234e-10)), "-1.234e-10")]
245    #[case::f32_inf(Box::new(|w: &mut OS| w.write_f32_value("a", f32::INFINITY)), "null")]
246    #[case::f32_neg_inf(Box::new(|w: &mut OS| w.write_f32_value("a", f32::NEG_INFINITY)), "null")]
247    #[case::f32_nan(Box::new(|w: &mut OS| w.write_f32_value("a", f32::NAN)), "null")]
248    fn test_write_value(#[case] code: Box<dyn Fn(&mut OS) -> io::Result<()>>, #[case] expected: &str) -> io::Result<()> {
249        {
250            let mut buf = Vec::new();
251            let mut writer = JsonWriter::new_compact(&mut buf);
252            {
253                let mut object_ser = JsonObject::new(&mut writer)?;
254                code(&mut object_ser)?;
255            }
256
257            let actual = String::from_utf8(buf).unwrap();
258            let expected = format!(r#"{}"a":{}{}"#, "{", expected, "}");
259            assert_eq!(actual, expected);
260        }
261
262        // test with and without preceding element to verify that 'initial' is handled correctly
263        {
264            let mut buf = Vec::new();
265            let mut writer = JsonWriter::new_compact(&mut buf);
266            {
267                let mut object_ser = JsonObject::new(&mut writer)?;
268                object_ser.write_null_value("x")?;
269                code(&mut object_ser)?;
270                object_ser.write_u32_value("y", 5)?;
271            }
272
273            let actual = String::from_utf8(buf).unwrap();
274            let expected = format!(r#"{}"x":null,"a":{},"y":5{}"#, "{", expected, "}");
275            assert_eq!(actual, expected);
276        }
277
278        Ok(())
279    }
280}