protoflow_blocks/blocks/io/
decode_json.rs

1// This is free and unencumbered software released into the public domain.
2
3extern crate std;
4
5use crate::{
6    prelude::{BTreeMap, Bytes, ToString, Vec},
7    StdioConfig, StdioError, StdioSystem, System,
8};
9use protoflow_core::{
10    types::Value, Block, BlockError, BlockResult, BlockRuntime, InputPort, OutputPort,
11};
12use protoflow_derive::Block;
13use simple_mermaid::mermaid;
14use struson::reader::*;
15
16/// A block that decodes a JSON format message from a byte stream.
17///
18/// # Block Diagram
19#[doc = mermaid!("../../../doc/io/decode_json.mmd")]
20///
21/// # Sequence Diagram
22#[doc = mermaid!("../../../doc/io/decode_json.seq.mmd" framed)]
23///
24/// # Examples
25///
26/// ## Using the block in a system
27///
28/// ```rust
29/// # use protoflow_blocks::*;
30/// # fn main() {
31/// System::build(|s| {
32///     // TODO
33/// });
34/// # }
35/// ```
36///
37/// ## Running the block via the CLI
38///
39/// ```console
40/// $ protoflow execute DecodeJSON
41/// ```
42///
43#[derive(Block, Clone)]
44pub struct DecodeJson {
45    /// The input message byte stream.
46    #[input]
47    pub input: InputPort<Bytes>,
48
49    /// The output JSON value.
50    #[output]
51    pub output: OutputPort<Value>,
52}
53
54impl DecodeJson {
55    pub fn new(input: InputPort<Bytes>, output: OutputPort<Value>) -> Self {
56        Self { input, output }
57    }
58
59    pub fn with_system(system: &System) -> Self {
60        use crate::SystemBuilding;
61        Self::new(system.input(), system.output())
62    }
63}
64
65impl Block for DecodeJson {
66    fn execute(&mut self, runtime: &dyn BlockRuntime) -> BlockResult {
67        runtime.wait_for(&self.input)?;
68
69        while let Some(input) = self.input.recv()? {
70            let output = {
71                let mut json = JsonStreamReader::new(input.as_ref());
72                decode(&mut json).map_err(|e| BlockError::Other(e.to_string()))?
73            };
74            self.output.send(&output)?;
75        }
76
77        Ok(())
78    }
79}
80
81pub fn decode(json: &mut JsonStreamReader<&[u8]>) -> Result<Value, ReaderError> {
82    use protoflow_core::types::{value::Kind::*, ListValue as LV, Struct};
83
84    let kind = match json.peek()? {
85        ValueType::Null => json.next_null().map(|_| NullValue(0))?,
86        ValueType::Boolean => json.next_bool().map(BoolValue)?,
87        ValueType::Number => json.next_number_as_string().and_then(|num_str| {
88            Ok(match num_str.as_str() {
89                "NaN" => NumberValue(f64::NAN),
90                "-Infinity" => NumberValue(f64::NEG_INFINITY),
91                "Infinity" => NumberValue(f64::INFINITY),
92                number => NumberValue(number.parse::<f64>().map_err(|_| {
93                    ReaderError::UnsupportedNumberValue {
94                        number: number.into(),
95                        location: json.current_position(false),
96                    }
97                })?),
98            })
99        })?,
100        ValueType::String => json.next_string().map(StringValue)?,
101        ValueType::Array => {
102            json.begin_array()?;
103
104            let mut values = Vec::new();
105            while json.has_next().unwrap() {
106                values.push(decode(json)?);
107            }
108
109            json.end_array()?;
110            ListValue(LV { values })
111        }
112        ValueType::Object => {
113            json.begin_object()?;
114            let mut fields = BTreeMap::new();
115            while json.has_next()? {
116                let key = json.next_name_owned()?;
117                let value = decode(json)?;
118                fields.insert(key, value);
119            }
120            json.end_object()?;
121            StructValue(Struct { fields })
122        }
123    };
124
125    Ok(Value { kind: Some(kind) })
126}
127
128#[cfg(feature = "std")]
129impl StdioSystem for DecodeJson {
130    fn build_system(config: StdioConfig) -> Result<System, StdioError> {
131        //use crate::{IoBlocks, SysBlocks, SystemBuilding};
132
133        config.reject_any()?;
134
135        Ok(System::build(|_s| {
136            //let stdin = s.read_stdin();
137            //let json_decoder = s.decode_json();
138            //let stdout = s.write_stdout();
139            //s.connect(&stdin.output, &json_decoder.input);
140            //s.connect(&json_decoder.output, &stdout.input);
141            todo!() // TODO
142        }))
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    extern crate std;
149
150    use super::{decode, DecodeJson};
151    use crate::{
152        prelude::{vec, BTreeMap, ToString},
153        System, SystemBuilding,
154    };
155
156    #[test]
157    fn instantiate_block() {
158        // Check that the block is constructible:
159        let _ = System::build(|s| {
160            let _ = s.block(DecodeJson::new(s.input(), s.output()));
161        });
162    }
163
164    #[test]
165    fn test_decode() {
166        use super::{JsonStreamReader, ReaderError, Value};
167
168        use protoflow_core::types::{
169            value::Kind::{self, *},
170            ListValue as LV, Struct,
171        };
172
173        fn to_value(kind: Kind) -> Value {
174            Value { kind: Some(kind) }
175        }
176
177        fn read_value(input: &str) -> Result<Value, ReaderError> {
178            let mut json = JsonStreamReader::new(input.as_ref());
179            decode(&mut json)
180        }
181
182        assert_eq!(to_value(NullValue(0)), read_value("null").unwrap());
183        assert_eq!(to_value(BoolValue(false)), read_value("false").unwrap());
184        assert_eq!(to_value(BoolValue(true)), read_value("true").unwrap());
185        assert_eq!(to_value(NumberValue(0.)), read_value("0").unwrap());
186        assert_eq!(to_value(NumberValue(1.)), read_value("1").unwrap());
187        assert_eq!(to_value(NumberValue(1.)), read_value("1").unwrap());
188        assert_eq!(to_value(NumberValue(1.)), read_value("1.0").unwrap());
189        assert_eq!(to_value(NumberValue(1.123)), read_value("1.123").unwrap());
190        assert_eq!(to_value(NumberValue(-1.123)), read_value("-1.123").unwrap());
191        assert_eq!(to_value(NumberValue(100.)), read_value("100").unwrap());
192        assert_eq!(to_value(NumberValue(1000.)), read_value("1e3").unwrap());
193
194        assert_eq!(
195            to_value(StringValue("Hello world".to_string())),
196            read_value(r#""Hello world""#).unwrap()
197        );
198
199        assert_eq!(
200            to_value(ListValue(LV { values: vec![] })),
201            read_value(r#"[]"#).unwrap()
202        );
203
204        assert_eq!(
205            to_value(ListValue(LV {
206                values: vec![
207                    NullValue(0),
208                    BoolValue(false),
209                    BoolValue(true),
210                    NumberValue(1.),
211                    StringValue("foo".to_string()),
212                    ListValue(LV {
213                        values: vec![to_value(StringValue("nested".to_string()))]
214                    }),
215                    StructValue(Struct {
216                        fields: {
217                            let mut obj = BTreeMap::new();
218                            obj.insert("foo".into(), to_value(NumberValue(1.)));
219                            obj.insert("bar".into(), to_value(BoolValue(true)));
220                            obj
221                        }
222                    }),
223                ]
224                .into_iter()
225                .map(to_value)
226                .collect()
227            })),
228            read_value(r#"[null, false, true, 1, "foo", ["nested"], {"foo": 1, "bar": true}]"#)
229                .unwrap()
230        );
231    }
232}