Skip to main content

json_streaming/nonblocking/
array.rs

1use crate::nonblocking::io::NonBlockingWrite;
2use crate::nonblocking::json_writer::JsonWriter;
3use crate::nonblocking::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: NonBlockingWrite, 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: NonBlockingWrite, 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 async fn new(writer: &'a mut JsonWriter<'b, W, F, FF>) -> Result<Self, W::Error> {
26        writer.write_bytes(b"[").await?;
27        writer.write_format_after_start_nested().await?;
28
29        Ok(JsonArray {
30            writer,
31            is_initial: true,
32            is_ended: false,
33        })
34    }
35
36    async 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",").await?;
42            self.writer.write_format_after_element().await?;
43        }
44        self.writer.write_format_indent().await?;
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 async fn unchecked_write_raw_value(&mut self, value_json: &str) -> Result<(), W::Error> {
54        self.handle_initial().await?;
55        self.writer.write_bytes(value_json.as_bytes()).await
56    }
57
58    /// Write an element of type 'string', escaping the provided string value.
59    pub async fn write_string_value(&mut self, value: &str) -> Result<(), W::Error> {
60        self.handle_initial().await?;
61        self.writer.write_escaped_string(value).await
62    }
63
64    /// Write an element of type 'bool'.
65    pub async fn write_bool_value(&mut self, value: bool) -> Result<(), W::Error> {
66        self.handle_initial().await?;
67        self.writer.write_bool(value).await
68    }
69
70    /// Write a null literal as an element.
71    pub async fn write_null_value(&mut self) -> Result<(), W::Error> {
72        self.handle_initial().await?;
73        self.writer.write_bytes(b"null").await
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 async fn write_f64_value(&mut self, value: f64) -> Result<(), W::Error> {
81        self.handle_initial().await?;
82        self.writer.write_f64(value).await
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 async fn write_f32_value(&mut self, value: f32) -> Result<(), W::Error> {
90        self.handle_initial().await?;
91        self.writer.write_f32(value).await
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 async 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().await?;
102        JsonObject::new(self.writer).await
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 async 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().await?;
113        JsonArray::new(self.writer).await
114    }
115
116    /// Explicitly end this array's lifetime and write the closing bracket.
117    pub async fn end(self) -> Result<(), W::Error> {
118        let mut mut_self = self;
119        mut_self._end().await
120    }
121
122    async fn _end(&mut self) -> Result<(), W::Error> {
123        self.writer.write_format_before_end_nested(self.is_initial).await?;
124        self.writer.write_bytes(b"]").await?;
125        self.is_ended = true;
126        Ok(())
127    }
128}
129
130macro_rules! write_arr_int {
131    ($t:ty ; $f:ident) => {
132impl<'a, 'b, W: NonBlockingWrite, 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 async fn $f(&mut self, value: $t) -> Result<(), W::Error> {
136        self.handle_initial().await?;
137        self.writer.write_raw_num(value).await
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
157
158#[cfg(test)]
159pub mod tests {
160    use super::*;
161    use crate::nonblocking::object::tests::ObjectCommand;
162    use rstest::*;
163    use std::io;
164
165    pub enum ArrayCommand {
166        Null,
167        Bool(bool),
168        Raw(&'static str),
169        String(&'static str),
170        U8(u8),
171        I8(i8),
172        U16(u16),
173        I16(i16),
174        U32(u32),
175        I32(i32),
176        U64(u64),
177        I64(i64),
178        U128(u128),
179        I128(i128),
180        Usize(usize),
181        Isize(isize),
182        F64(f64),
183        F32(f32),
184        Object(Vec<ObjectCommand>),
185        Array(Vec<ArrayCommand>)
186    }
187    impl ArrayCommand {
188        pub async fn apply(&self, arr: &mut JsonArray<'_, '_, Vec<u8>, CompactFormatter, DefaultFloatFormat>) {
189            match self {
190                ArrayCommand::Null => arr.write_null_value().await.unwrap(),
191                ArrayCommand::Bool(b) => arr.write_bool_value(*b).await.unwrap(),
192                ArrayCommand::Raw(s) => arr.unchecked_write_raw_value(s).await.unwrap(),
193                ArrayCommand::String(s) => arr.write_string_value(s).await.unwrap(),
194                ArrayCommand::U8(n) => arr.write_u8_value(*n).await.unwrap(),
195                ArrayCommand::I8(n) => arr.write_i8_value(*n).await.unwrap(),
196                ArrayCommand::U16(n) => arr.write_u16_value(*n).await.unwrap(),
197                ArrayCommand::I16(n) => arr.write_i16_value(*n).await.unwrap(),
198                ArrayCommand::U32(n) => arr.write_u32_value(*n).await.unwrap(),
199                ArrayCommand::I32(n) => arr.write_i32_value(*n).await.unwrap(),
200                ArrayCommand::U64(n) => arr.write_u64_value(*n).await.unwrap(),
201                ArrayCommand::I64(n) => arr.write_i64_value(*n).await.unwrap(),
202                ArrayCommand::U128(n) => arr.write_u128_value(*n).await.unwrap(),
203                ArrayCommand::I128(n) => arr.write_i128_value(*n).await.unwrap(),
204                ArrayCommand::Usize(n) => arr.write_usize_value(*n).await.unwrap(),
205                ArrayCommand::Isize(n) => arr.write_isize_value(*n).await.unwrap(),
206                ArrayCommand::F64(x) => arr.write_f64_value(*x).await.unwrap(),
207                ArrayCommand::F32(x) => arr.write_f32_value(*x).await.unwrap(),
208                ArrayCommand::Object(cmds) => {
209                    let mut nested = arr.start_object().await.unwrap();
210                    for cmd in cmds {
211                        Box::pin(cmd.apply(&mut nested)).await;
212                    }
213                    nested.end().await.unwrap();
214                }
215                ArrayCommand::Array(cmds) => {
216                    let mut nested = arr.start_array().await.unwrap();
217                    for cmd in cmds {
218                        Box::pin(cmd.apply(&mut nested)).await;
219                    }
220                    nested.end().await.unwrap();
221                }
222            }
223        }
224    }
225
226    #[rstest]
227    #[case::empty(vec![], "[]")]
228    #[case::single(vec![ArrayCommand::Null], "[null]")]
229    #[case::two(vec![ArrayCommand::U32(1), ArrayCommand::U32(2)], "[1,2]")]
230    #[case::nested_arr(vec![ArrayCommand::Array(vec![])], "[[]]")]
231    #[case::nested_arr_with_el(vec![ArrayCommand::Array(vec![ArrayCommand::U32(5)])], "[[5]]")]
232    #[case::nested_arr_first(vec![ArrayCommand::Array(vec![]), ArrayCommand::U32(4)], "[[],4]")]
233    #[case::nested_arr_last(vec![ArrayCommand::U32(6), ArrayCommand::Array(vec![])], "[6,[]]")]
234    #[case::nested_arr_between(vec![ArrayCommand::U32(7), ArrayCommand::Array(vec![]), ArrayCommand::U32(9)], "[7,[],9]")]
235    #[case::two_nested_arrays(vec![ArrayCommand::Array(vec![]), ArrayCommand::Array(vec![])], "[[],[]]")]
236    #[case::nested_obj(vec![ArrayCommand::Object(vec![])], "[{}]")]
237    #[case::nested_obj_with_el(vec![ArrayCommand::Object(vec![ObjectCommand::U32("a", 3)])], r#"[{"a":3}]"#)]
238    #[case::nested_obj_first(vec![ArrayCommand::Object(vec![]), ArrayCommand::U32(0)], r#"[{},0]"#)]
239    #[case::nested_obj_last(vec![ArrayCommand::U32(2), ArrayCommand::Object(vec![])], r#"[2,{}]"#)]
240    #[case::two_nested_objects(vec![ArrayCommand::Object(vec![]), ArrayCommand::Object(vec![])], r#"[{},{}]"#)]
241    #[tokio::test]
242    async fn test_array(#[case] code: Vec<ArrayCommand>, #[case] expected: &str) -> io::Result<()> {
243        let mut buf = Vec::new();
244        let mut writer = JsonWriter::new_compact(&mut buf);
245        let mut array_ser = JsonArray::new(&mut writer).await?;
246        for cmd in code {
247            cmd.apply(&mut array_ser).await;
248        }
249        array_ser.end().await?;
250
251        let actual = String::from_utf8(buf).unwrap();
252        assert_eq!(actual, expected);
253        Ok(())
254    }
255
256    #[rstest]
257    #[case::null(ArrayCommand::Null, "null")]
258    #[case::bool_true(ArrayCommand::Bool(true), "true")]
259    #[case::bool_false(ArrayCommand::Bool(false), "false")]
260    #[case::raw(ArrayCommand::Raw("x y z"), "x y z")]
261    #[case::string(ArrayCommand::String("asdf"), r#""asdf""#)]
262    #[case::string_escaped(ArrayCommand::String("\r\n"), r#""\r\n""#)]
263    #[case::u8(ArrayCommand::U8(2), "2")]
264    #[case::i8(ArrayCommand::I8(-3), "-3")]
265    #[case::u16(ArrayCommand::U16(4), "4")]
266    #[case::i16(ArrayCommand::I16(-5), "-5")]
267    #[case::u32(ArrayCommand::U32(6), "6")]
268    #[case::i32(ArrayCommand::I32(-7), "-7")]
269    #[case::u64(ArrayCommand::U64(8), "8")]
270    #[case::i64(ArrayCommand::I64(-9), "-9")]
271    #[case::u128(ArrayCommand::U128(10), "10")]
272    #[case::i128(ArrayCommand::I128(-11), "-11")]
273    #[case::u128(ArrayCommand::Usize(12), "12")]
274    #[case::i128(ArrayCommand::Isize(-13), "-13")]
275    #[case::f64(ArrayCommand::F64(2.0), "2")]
276    #[case::f64_exp_5(ArrayCommand::F64(1.234e5), "123400")]
277    #[case::f64_exp_10(ArrayCommand::F64(1.234e10), "1.234e10")]
278    #[case::f64_exp_20(ArrayCommand::F64(1.234e20), "1.234e20")]
279    #[case::f64_exp_neg_3(ArrayCommand::F64(1.234e-3), "0.001234")]
280    #[case::f64_exp_neg_10(ArrayCommand::F64(1.234e-10), "1.234e-10")]
281    #[case::f64_neg(ArrayCommand::F64(-2.0), "-2")]
282    #[case::f64_neg_exp_5(ArrayCommand::F64(-1.234e5), "-123400")]
283    #[case::f64_neg_exp_10(ArrayCommand::F64(-1.234e10), "-1.234e10")]
284    #[case::f64_neg_exp_20(ArrayCommand::F64(-1.234e20), "-1.234e20")]
285    #[case::f64_neg_exp_neg_3(ArrayCommand::F64(-1.234e-3), "-0.001234")]
286    #[case::f64_neg_exp_neg_10(ArrayCommand::F64(-1.234e-10), "-1.234e-10")]
287    #[case::f64_inf(ArrayCommand::F64(f64::INFINITY), "null")]
288    #[case::f64_neg_inf(ArrayCommand::F64(f64::NEG_INFINITY), "null")]
289    #[case::f64_nan(ArrayCommand::F64(f64::NAN), "null")]
290    #[case::f32(ArrayCommand::F32(2.0), "2")]
291    #[case::f32_exp_5(ArrayCommand::F32(1.234e5), "123400")]
292    #[case::f32_exp_10(ArrayCommand::F32(1.234e10), "1.234e10")]
293    #[case::f32_exp_20(ArrayCommand::F32(1.234e20), "1.234e20")]
294    #[case::f32_exp_neg_3(ArrayCommand::F32(1.234e-3), "0.001234")]
295    #[case::f32_exp_neg_10(ArrayCommand::F32(1.234e-10), "1.234e-10")]
296    #[case::f32_neg(ArrayCommand::F32(-2.0), "-2")]
297    #[case::f32_neg_exp_5(ArrayCommand::F32(-1.234e5), "-123400")]
298    #[case::f32_neg_exp_10(ArrayCommand::F32(-1.234e10), "-1.234e10")]
299    #[case::f32_neg_exp_20(ArrayCommand::F32(-1.234e20), "-1.234e20")]
300    #[case::f32_neg_exp_neg_3(ArrayCommand::F32(-1.234e-3), "-0.001234")]
301    #[case::f32_neg_exp_neg_10(ArrayCommand::F32(-1.234e-10), "-1.234e-10")]
302    #[case::f32_inf(ArrayCommand::F32(f32::INFINITY), "null")]
303    #[case::f32_neg_inf(ArrayCommand::F32(f32::NEG_INFINITY), "null")]
304    #[case::f32_nan(ArrayCommand::F32(f32::NAN), "null")]
305    #[tokio::test]
306    async fn test_write_value(#[case] cmd: ArrayCommand, #[case] expected: &str) -> io::Result<()> {
307        {
308            let mut buf = Vec::new();
309            let mut writer = JsonWriter::new_compact(&mut buf);
310            {
311                let mut array_ser = JsonArray::new(&mut writer).await?;
312                cmd.apply(&mut array_ser).await;
313                array_ser.end().await?;
314            }
315
316            let actual = String::from_utf8(buf).unwrap();
317            let expected = format!("[{}]", expected);
318            assert_eq!(actual, expected);
319        }
320
321        // test with and without preceding element to verify that 'initial' is handled correctly
322        {
323            let mut buf = Vec::new();
324            let mut writer = JsonWriter::new_compact(&mut buf);
325            {
326                let mut array_ser = JsonArray::new(&mut writer).await?;
327                array_ser.write_null_value().await?;
328                cmd.apply(&mut array_ser).await;
329                array_ser.end().await?;
330            }
331
332            let actual = String::from_utf8(buf).unwrap();
333            let expected = format!("[null,{}]", expected);
334            assert_eq!(actual, expected);
335        }
336
337        Ok(())
338    }
339}