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 an element of type 'string', escaping the provided string value.
49    pub async fn write_string_value(&mut self, value: &str) -> Result<(), W::Error> {
50        self.handle_initial().await?;
51        self.writer.write_escaped_string(value).await
52    }
53
54    /// Write an element of type 'bool'.
55    pub async fn write_bool_value(&mut self, value: bool) -> Result<(), W::Error> {
56        self.handle_initial().await?;
57        self.writer.write_bool(value).await
58    }
59
60    /// Write a null literal as an element.
61    pub async fn write_null_value(&mut self) -> Result<(), W::Error> {
62        self.handle_initial().await?;
63        self.writer.write_bytes(b"null").await
64    }
65
66    /// Write an f64 value as an element. If the value is not finite (i.e. infinite or NaN),
67    ///  a null literal is written instead. Different behavior (e.g. leaving out the element
68    ///  for non-finite numbers, representing them in some other way etc.) is the responsibility
69    ///  of application code.
70    pub async fn write_f64_value(&mut self, value: f64) -> Result<(), W::Error> {
71        self.handle_initial().await?;
72        self.writer.write_f64(value).await
73    }
74
75    /// Write an f32 value as an element. If the value is not finite (i.e. infinite or NaN),
76    ///  a null literal is written instead. Different behavior (e.g. leaving out the element
77    ///  for non-finite numbers, representing them in some other way etc.) is the responsibility
78    ///  of application code.
79    pub async fn write_f32_value(&mut self, value: f32) -> Result<(), W::Error> {
80        self.handle_initial().await?;
81        self.writer.write_f32(value).await
82    }
83
84    /// Start a nested object as an element. This function returns a new [JsonObject] instance
85    ///  for writing elements to the nested object. When the returned [JsonObject] goes out of scope
86    ///  (per syntactic scope or an explicit call to `end()`), the nested object is closed, and
87    ///  application code can continue adding elements to the owning `self` object.
88    pub async fn start_object<'c, 'x>(&'x mut self) -> Result<JsonObject<'c, 'b, W, F, FF>, W::Error>
89    where 'a: 'c, 'x: 'c
90    {
91        self.handle_initial().await?;
92        JsonObject::new(self.writer).await
93    }
94
95    /// Start a nested array as an element. This function returns a new [JsonArray] instance
96    ///  for writing elements to the nested object. When the returned [JsonArray] goes out of scope
97    ///  (per syntactic scope or an explicit call to `end()`), the nested object is closed, and
98    ///  application code can continue adding elements to the owning `self` object.
99    pub async fn start_array<'c, 'x>(&'x mut self) -> Result<JsonArray<'c, 'b, W, F, FF>, W::Error>
100    where 'a: 'c, 'x: 'c
101    {
102        self.handle_initial().await?;
103        JsonArray::new(self.writer).await
104    }
105
106    /// Explicitly end this array's lifetime and write the closing bracket.
107    pub async fn end(self) -> Result<(), W::Error> {
108        let mut mut_self = self;
109        mut_self._end().await
110    }
111
112    async fn _end(&mut self) -> Result<(), W::Error> {
113        self.writer.write_format_before_end_nested(self.is_initial).await?;
114        self.writer.write_bytes(b"]").await?;
115        self.is_ended = true;
116        Ok(())
117    }
118}
119
120macro_rules! write_arr_int {
121    ($t:ty ; $f:ident) => {
122impl<'a, 'b, W: NonBlockingWrite, F: JsonFormatter, FF: FloatFormat> JsonArray<'a, 'b, W, F, FF> {
123    /// Write an element with a generic int value. This function fits most Rust integral
124    ///  types; for the exceptions, there are separate functions.
125    pub async fn $f(&mut self, value: $t) -> Result<(), W::Error> {
126        self.handle_initial().await?;
127        self.writer.write_raw_num(value).await
128    }
129}
130    };
131}
132write_arr_int!(i8; write_i8_value);
133write_arr_int!(u8; write_u8_value);
134write_arr_int!(i16; write_i16_value);
135write_arr_int!(u16; write_u16_value);
136write_arr_int!(i32; write_i32_value);
137write_arr_int!(u32; write_u32_value);
138write_arr_int!(i64; write_i64_value);
139write_arr_int!(u64; write_u64_value);
140write_arr_int!(i128; write_i128_value);
141write_arr_int!(u128; write_u128_value);
142write_arr_int!(isize; write_isize_value);
143write_arr_int!(usize; write_usize_value);
144
145
146
147
148#[cfg(test)]
149pub mod tests {
150    use super::*;
151    use crate::nonblocking::object::tests::ObjectCommand;
152    use rstest::*;
153    use std::io;
154
155    pub enum ArrayCommand {
156        Null,
157        Bool(bool),
158        String(&'static str),
159        U8(u8),
160        I8(i8),
161        U16(u16),
162        I16(i16),
163        U32(u32),
164        I32(i32),
165        U64(u64),
166        I64(i64),
167        U128(u128),
168        I128(i128),
169        Usize(usize),
170        Isize(isize),
171        F64(f64),
172        F32(f32),
173        Object(Vec<ObjectCommand>),
174        Array(Vec<ArrayCommand>)
175    }
176    impl ArrayCommand {
177        pub async fn apply(&self, arr: &mut JsonArray<'_, '_, Vec<u8>, CompactFormatter, DefaultFloatFormat>) {
178            match self {
179                ArrayCommand::Null => arr.write_null_value().await.unwrap(),
180                ArrayCommand::Bool(b) => arr.write_bool_value(*b).await.unwrap(),
181                ArrayCommand::String(s) => arr.write_string_value(s).await.unwrap(),
182                ArrayCommand::U8(n) => arr.write_u8_value(*n).await.unwrap(),
183                ArrayCommand::I8(n) => arr.write_i8_value(*n).await.unwrap(),
184                ArrayCommand::U16(n) => arr.write_u16_value(*n).await.unwrap(),
185                ArrayCommand::I16(n) => arr.write_i16_value(*n).await.unwrap(),
186                ArrayCommand::U32(n) => arr.write_u32_value(*n).await.unwrap(),
187                ArrayCommand::I32(n) => arr.write_i32_value(*n).await.unwrap(),
188                ArrayCommand::U64(n) => arr.write_u64_value(*n).await.unwrap(),
189                ArrayCommand::I64(n) => arr.write_i64_value(*n).await.unwrap(),
190                ArrayCommand::U128(n) => arr.write_u128_value(*n).await.unwrap(),
191                ArrayCommand::I128(n) => arr.write_i128_value(*n).await.unwrap(),
192                ArrayCommand::Usize(n) => arr.write_usize_value(*n).await.unwrap(),
193                ArrayCommand::Isize(n) => arr.write_isize_value(*n).await.unwrap(),
194                ArrayCommand::F64(x) => arr.write_f64_value(*x).await.unwrap(),
195                ArrayCommand::F32(x) => arr.write_f32_value(*x).await.unwrap(),
196                ArrayCommand::Object(cmds) => {
197                    let mut nested = arr.start_object().await.unwrap();
198                    for cmd in cmds {
199                        Box::pin(cmd.apply(&mut nested)).await;
200                    }
201                    nested.end().await.unwrap();
202                }
203                ArrayCommand::Array(cmds) => {
204                    let mut nested = arr.start_array().await.unwrap();
205                    for cmd in cmds {
206                        Box::pin(cmd.apply(&mut nested)).await;
207                    }
208                    nested.end().await.unwrap();
209                }
210            }
211        }
212    }
213
214    #[rstest]
215    #[case::empty(vec![], "[]")]
216    #[case::single(vec![ArrayCommand::Null], "[null]")]
217    #[case::two(vec![ArrayCommand::U32(1), ArrayCommand::U32(2)], "[1,2]")]
218    #[case::nested_arr(vec![ArrayCommand::Array(vec![])], "[[]]")]
219    #[case::nested_arr_with_el(vec![ArrayCommand::Array(vec![ArrayCommand::U32(5)])], "[[5]]")]
220    #[case::nested_arr_first(vec![ArrayCommand::Array(vec![]), ArrayCommand::U32(4)], "[[],4]")]
221    #[case::nested_arr_last(vec![ArrayCommand::U32(6), ArrayCommand::Array(vec![])], "[6,[]]")]
222    #[case::nested_arr_between(vec![ArrayCommand::U32(7), ArrayCommand::Array(vec![]), ArrayCommand::U32(9)], "[7,[],9]")]
223    #[case::two_nested_arrays(vec![ArrayCommand::Array(vec![]), ArrayCommand::Array(vec![])], "[[],[]]")]
224    #[case::nested_obj(vec![ArrayCommand::Object(vec![])], "[{}]")]
225    #[case::nested_obj_with_el(vec![ArrayCommand::Object(vec![ObjectCommand::U32("a", 3)])], r#"[{"a":3}]"#)]
226    #[case::nested_obj_first(vec![ArrayCommand::Object(vec![]), ArrayCommand::U32(0)], r#"[{},0]"#)]
227    #[case::nested_obj_last(vec![ArrayCommand::U32(2), ArrayCommand::Object(vec![])], r#"[2,{}]"#)]
228    #[case::two_nested_objects(vec![ArrayCommand::Object(vec![]), ArrayCommand::Object(vec![])], r#"[{},{}]"#)]
229    #[tokio::test]
230    async fn test_array(#[case] code: Vec<ArrayCommand>, #[case] expected: &str) -> io::Result<()> {
231        let mut buf = Vec::new();
232        let mut writer = JsonWriter::new_compact(&mut buf);
233        let mut array_ser = JsonArray::new(&mut writer).await?;
234        for cmd in code {
235            cmd.apply(&mut array_ser).await;
236        }
237        array_ser.end().await?;
238
239        let actual = String::from_utf8(buf).unwrap();
240        assert_eq!(actual, expected);
241        Ok(())
242    }
243
244    #[rstest]
245    #[case::null(ArrayCommand::Null, "null")]
246    #[case::bool_true(ArrayCommand::Bool(true), "true")]
247    #[case::bool_false(ArrayCommand::Bool(false), "false")]
248    #[case::string(ArrayCommand::String("asdf"), r#""asdf""#)]
249    #[case::string_escaped(ArrayCommand::String("\r\n"), r#""\r\n""#)]
250    #[case::u8(ArrayCommand::U8(2), "2")]
251    #[case::i8(ArrayCommand::I8(-3), "-3")]
252    #[case::u16(ArrayCommand::U16(4), "4")]
253    #[case::i16(ArrayCommand::I16(-5), "-5")]
254    #[case::u32(ArrayCommand::U32(6), "6")]
255    #[case::i32(ArrayCommand::I32(-7), "-7")]
256    #[case::u64(ArrayCommand::U64(8), "8")]
257    #[case::i64(ArrayCommand::I64(-9), "-9")]
258    #[case::u128(ArrayCommand::U128(10), "10")]
259    #[case::i128(ArrayCommand::I128(-11), "-11")]
260    #[case::u128(ArrayCommand::Usize(12), "12")]
261    #[case::i128(ArrayCommand::Isize(-13), "-13")]
262    #[case::f64(ArrayCommand::F64(2.0), "2")]
263    #[case::f64_exp_5(ArrayCommand::F64(1.234e5), "123400")]
264    #[case::f64_exp_10(ArrayCommand::F64(1.234e10), "1.234e10")]
265    #[case::f64_exp_20(ArrayCommand::F64(1.234e20), "1.234e20")]
266    #[case::f64_exp_neg_3(ArrayCommand::F64(1.234e-3), "0.001234")]
267    #[case::f64_exp_neg_10(ArrayCommand::F64(1.234e-10), "1.234e-10")]
268    #[case::f64_neg(ArrayCommand::F64(-2.0), "-2")]
269    #[case::f64_neg_exp_5(ArrayCommand::F64(-1.234e5), "-123400")]
270    #[case::f64_neg_exp_10(ArrayCommand::F64(-1.234e10), "-1.234e10")]
271    #[case::f64_neg_exp_20(ArrayCommand::F64(-1.234e20), "-1.234e20")]
272    #[case::f64_neg_exp_neg_3(ArrayCommand::F64(-1.234e-3), "-0.001234")]
273    #[case::f64_neg_exp_neg_10(ArrayCommand::F64(-1.234e-10), "-1.234e-10")]
274    #[case::f64_inf(ArrayCommand::F64(f64::INFINITY), "null")]
275    #[case::f64_neg_inf(ArrayCommand::F64(f64::NEG_INFINITY), "null")]
276    #[case::f64_nan(ArrayCommand::F64(f64::NAN), "null")]
277    #[case::f32(ArrayCommand::F32(2.0), "2")]
278    #[case::f32_exp_5(ArrayCommand::F32(1.234e5), "123400")]
279    #[case::f32_exp_10(ArrayCommand::F32(1.234e10), "1.234e10")]
280    #[case::f32_exp_20(ArrayCommand::F32(1.234e20), "1.234e20")]
281    #[case::f32_exp_neg_3(ArrayCommand::F32(1.234e-3), "0.001234")]
282    #[case::f32_exp_neg_10(ArrayCommand::F32(1.234e-10), "1.234e-10")]
283    #[case::f32_neg(ArrayCommand::F32(-2.0), "-2")]
284    #[case::f32_neg_exp_5(ArrayCommand::F32(-1.234e5), "-123400")]
285    #[case::f32_neg_exp_10(ArrayCommand::F32(-1.234e10), "-1.234e10")]
286    #[case::f32_neg_exp_20(ArrayCommand::F32(-1.234e20), "-1.234e20")]
287    #[case::f32_neg_exp_neg_3(ArrayCommand::F32(-1.234e-3), "-0.001234")]
288    #[case::f32_neg_exp_neg_10(ArrayCommand::F32(-1.234e-10), "-1.234e-10")]
289    #[case::f32_inf(ArrayCommand::F32(f32::INFINITY), "null")]
290    #[case::f32_neg_inf(ArrayCommand::F32(f32::NEG_INFINITY), "null")]
291    #[case::f32_nan(ArrayCommand::F32(f32::NAN), "null")]
292    #[tokio::test]
293    async fn test_write_value(#[case] cmd: ArrayCommand, #[case] expected: &str) -> io::Result<()> {
294        {
295            let mut buf = Vec::new();
296            let mut writer = JsonWriter::new_compact(&mut buf);
297            {
298                let mut array_ser = JsonArray::new(&mut writer).await?;
299                cmd.apply(&mut array_ser).await;
300                array_ser.end().await?;
301            }
302
303            let actual = String::from_utf8(buf).unwrap();
304            let expected = format!("[{}]", expected);
305            assert_eq!(actual, expected);
306        }
307
308        // test with and without preceding element to verify that 'initial' is handled correctly
309        {
310            let mut buf = Vec::new();
311            let mut writer = JsonWriter::new_compact(&mut buf);
312            {
313                let mut array_ser = JsonArray::new(&mut writer).await?;
314                array_ser.write_null_value().await?;
315                cmd.apply(&mut array_ser).await;
316                array_ser.end().await?;
317            }
318
319            let actual = String::from_utf8(buf).unwrap();
320            let expected = format!("[null,{}]", expected);
321            assert_eq!(actual, expected);
322        }
323
324        Ok(())
325    }
326}