Skip to main content

json_streaming/blocking/
array.rs

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