facet_json/
deserialize.rs

1use alloc::string::String;
2use alloc::string::ToString;
3use alloc::vec;
4use facet_core::Characteristic;
5use facet_core::Def;
6use facet_core::Facet;
7use facet_reflect::ReflectError;
8use facet_reflect::{HeapValue, Wip};
9use log::trace;
10use owo_colors::OwoColorize;
11
12mod tokenizer;
13pub use tokenizer::*;
14
15mod error;
16pub use error::*;
17
18/// Deserializes a JSON string into a value of type `T` that implements `Facet`.
19///
20/// This function takes a JSON string representation and converts it into a Rust
21/// value of the specified type `T`. The type must implement the `Facet` trait
22/// to provide the necessary type information for deserialization.
23pub fn from_str<'input, 'facet, T>(json: &'input str) -> Result<T, JsonError<'input>>
24where
25    T: Facet<'facet>,
26    'input: 'facet,
27{
28    from_slice(json.as_bytes())
29}
30
31/// Deserialize JSON from a slice
32///
33/// # Arguments
34///
35/// * `json` - A slice of bytes representing the JSON input.
36///
37/// # Returns
38///
39/// A result containing the deserialized value of type `T` or a `JsonParseErrorWithContext`.
40pub fn from_slice<'input, 'facet, T>(json: &'input [u8]) -> Result<T, JsonError<'input>>
41where
42    T: Facet<'facet>,
43    'input: 'facet,
44{
45    let wip = Wip::alloc::<T>().map_err(|e| {
46        JsonError::new(
47            JsonErrorKind::ReflectError(e),
48            json,
49            Span::new(0, json.len()),
50            "$".to_string(),
51        )
52    })?;
53    let heap_value = from_slice_wip(wip, json)?;
54    Ok(heap_value.materialize::<T>().unwrap())
55}
56
57/// Represents the next expected token or structure while parsing.
58#[derive(Debug, Clone, Copy, PartialEq, Eq)]
59enum Instruction {
60    Value,
61    SkipValue,
62    Pop(PopReason),
63    ObjectKeyOrObjectClose,
64    CommaThenObjectKeyOrObjectClose,
65    ArrayItemOrArrayClose,
66    CommaThenArrayItemOrArrayClose,
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70enum PopReason {
71    TopLevel,
72    ObjectVal,
73    ArrayItem,
74    Some,
75}
76
77/// Deserialize a JSON string into a Wip object.
78///
79/// # Arguments
80///
81/// * `wip` - A mutable Wip object to deserialize into.
82/// * `input` - A byte slice representing the JSON input.
83///
84/// # Returns
85///
86/// A result containing the updated `Wip` or a `JsonParseErrorWithContext`.
87pub fn from_slice_wip<'input: 'facet, 'facet>(
88    mut wip: Wip<'facet>,
89    input: &'input [u8],
90) -> Result<HeapValue<'facet>, JsonError<'input>> {
91    let mut stack = vec![Instruction::Pop(PopReason::TopLevel), Instruction::Value];
92    let mut tokenizer = Tokenizer::new(input);
93    let mut last_span = Span { start: 0, len: 0 };
94    let mut unread_token: Option<Spanned<Token>> = None;
95
96    macro_rules! bail {
97        ($kind:expr) => {
98            return Err(JsonError::new($kind, input, last_span, wip.path()))
99        };
100    }
101
102    macro_rules! read_token {
103        () => {
104            if let Some(token) = unread_token.take() {
105                last_span = token.span;
106                token
107            } else {
108                match tokenizer.next_token() {
109                    Ok(token) => {
110                        last_span = token.span;
111                        token
112                    }
113                    Err(e) => {
114                        last_span = e.span;
115                        bail!(JsonErrorKind::SyntaxError(e.kind));
116                    }
117                }
118            }
119        };
120    }
121
122    macro_rules! put_back_token {
123        ($token:expr) => {
124            assert!(
125                unread_token.is_none(),
126                "Cannot put back more than one token at a time"
127            );
128            unread_token = Some($token);
129        };
130    }
131
132    macro_rules! reflect {
133        ($($tt:tt)*) => {
134            let path = wip.path();
135            wip = match wip.$($tt)* {
136                Ok(wip) => wip,
137                Err(e) => {
138                    return Err(JsonError::new(
139                        JsonErrorKind::ReflectError(e),
140                        input,
141                        last_span,
142                        path,
143                    ));
144                }
145            }
146        };
147    }
148
149    loop {
150        let frame_count = wip.frames_count();
151        let insn = match stack.pop() {
152            Some(insn) => insn,
153            None => unreachable!("Instruction stack is empty"),
154        };
155        trace!("[{frame_count}] Instruction {:?}", insn.yellow());
156
157        match insn {
158            Instruction::Pop(reason) => {
159                trace!("Popping because {:?}", reason.yellow());
160
161                let container_shape = wip.shape();
162                match container_shape.def {
163                    Def::Struct(sd) => {
164                        let mut has_unset = false;
165
166                        trace!("Let's check all fields are initialized");
167                        for (index, field) in sd.fields.iter().enumerate() {
168                            let is_set = wip.is_field_set(index).map_err(|err| {
169                                trace!("Error checking field set status: {:?}", err);
170                                JsonError::new(
171                                    JsonErrorKind::ReflectError(err),
172                                    input,
173                                    last_span,
174                                    wip.path(),
175                                )
176                            })?;
177                            if !is_set {
178                                if let Some(default_in_place_fn) = field.maybe_default_fn() {
179                                    reflect!(field(index));
180                                    if let Some(default_in_place_fn) = default_in_place_fn {
181                                        reflect!(put_from_fn(default_in_place_fn));
182                                        trace!(
183                                            "Field #{} {:?} was set to default value (via custom fn)",
184                                            index.yellow(),
185                                            field.blue()
186                                        );
187                                    } else {
188                                        if !field.shape().is(Characteristic::Default) {
189                                            return Err(JsonError::new(
190                                                JsonErrorKind::ReflectError(
191                                                    ReflectError::DefaultAttrButNoDefaultImpl {
192                                                        shape: field.shape(),
193                                                    },
194                                                ),
195                                                input,
196                                                last_span,
197                                                wip.path(),
198                                            ));
199                                        }
200                                        reflect!(put_default());
201                                        trace!(
202                                            "Field #{} {:?} was set to default value (via default impl)",
203                                            index.yellow(),
204                                            field.blue()
205                                        );
206                                    }
207                                    reflect!(pop());
208                                } else {
209                                    trace!(
210                                        "Field #{} {:?} is not initialized",
211                                        index.yellow(),
212                                        field.blue()
213                                    );
214                                    has_unset = true;
215                                }
216                            }
217                        }
218
219                        if has_unset && container_shape.has_default_attr() {
220                            // let's allocate and build a default value
221                            let default_val = Wip::<'facet>::alloc_shape(container_shape)
222                                .map_err(|e| {
223                                    JsonError::new(
224                                        JsonErrorKind::ReflectError(e),
225                                        input,
226                                        last_span,
227                                        wip.path(),
228                                    )
229                                })?
230                                .put_default()
231                                .map_err(|e| {
232                                    JsonError::new(
233                                        JsonErrorKind::ReflectError(e),
234                                        input,
235                                        last_span,
236                                        wip.path(),
237                                    )
238                                })?
239                                .build()
240                                .map_err(|e| {
241                                    JsonError::new(
242                                        JsonErrorKind::ReflectError(e),
243                                        input,
244                                        last_span,
245                                        wip.path(),
246                                    )
247                                })?;
248                            let peek = default_val.peek().into_struct().unwrap();
249
250                            for (index, field) in sd.fields.iter().enumerate() {
251                                let is_set = wip.is_field_set(index).map_err(|err| {
252                                    trace!("Error checking field set status: {:?}", err);
253                                    JsonError::new(
254                                        JsonErrorKind::ReflectError(err),
255                                        input,
256                                        last_span,
257                                        wip.path(),
258                                    )
259                                })?;
260                                if !is_set {
261                                    let address_of_field_from_default =
262                                        peek.field(index).unwrap().data();
263                                    reflect!(field(index));
264                                    reflect!(put_shape(
265                                        address_of_field_from_default,
266                                        field.shape()
267                                    ));
268                                    reflect!(pop());
269                                }
270                            }
271                        }
272                    }
273                    Def::Enum(_) => {
274                        trace!(
275                            "TODO: make sure enums are initialized (support container-level and field-level default, etc.)"
276                        );
277                    }
278                    _ => {
279                        trace!("Thing being popped is not a container I guess");
280                    }
281                }
282
283                if reason == PopReason::TopLevel {
284                    let path = wip.path();
285                    return Ok(match wip.build() {
286                        Ok(hv) => hv,
287                        Err(e) => {
288                            return Err(JsonError::new(
289                                JsonErrorKind::ReflectError(e),
290                                input,
291                                last_span,
292                                path,
293                            ));
294                        }
295                    });
296                } else {
297                    reflect!(pop());
298                }
299            }
300            Instruction::SkipValue => {
301                let token = read_token!();
302                match token.node {
303                    Token::LBrace | Token::LBracket => {
304                        // Skip a compound value by tracking nesting depth
305                        let mut depth = 1;
306                        while depth > 0 {
307                            let token = read_token!();
308                            match token.node {
309                                Token::LBrace | Token::LBracket => {
310                                    depth += 1;
311                                }
312                                Token::RBrace | Token::RBracket => {
313                                    depth -= 1;
314                                }
315                                _ => {
316                                    // primitives, commas, colons, strings, numbers, etc.
317                                }
318                            }
319                        }
320                    }
321                    Token::String(_)
322                    | Token::F64(_)
323                    | Token::I64(_)
324                    | Token::U64(_)
325                    | Token::True
326                    | Token::False
327                    | Token::Null => {
328                        // Primitive value; nothing more to skip
329                    }
330                    other => {
331                        // Unexpected token when skipping a value
332                        bail!(JsonErrorKind::UnexpectedToken {
333                            got: other,
334                            wanted: "value"
335                        });
336                    }
337                }
338            }
339            Instruction::Value => {
340                let token = read_token!();
341                match token.node {
342                    Token::Null => {
343                        reflect!(put_default());
344                    }
345                    _ => {
346                        if matches!(wip.shape().def, Def::Option(_)) {
347                            trace!("Starting Some(_) option for {}", wip.shape().blue());
348                            reflect!(push_some());
349                            stack.push(Instruction::Pop(PopReason::Some))
350                        }
351
352                        match token.node {
353                            Token::Null => unreachable!(),
354                            Token::LBrace => {
355                                match wip.innermost_shape().def {
356                                    Def::Map(_md) => {
357                                        trace!(
358                                            "Object starting for map value ({})!",
359                                            wip.shape().blue()
360                                        );
361                                        reflect!(put_default());
362                                    }
363                                    Def::Enum(_ed) => {
364                                        trace!(
365                                            "Object starting for enum value ({})!",
366                                            wip.shape().blue()
367                                        );
368                                        // nothing to do here
369                                    }
370                                    Def::Struct(_) => {
371                                        trace!(
372                                            "Object starting for struct value ({})!",
373                                            wip.shape().blue()
374                                        );
375                                        // nothing to do here
376                                    }
377                                    _ => {
378                                        bail!(JsonErrorKind::UnsupportedType {
379                                            got: wip.innermost_shape(),
380                                            wanted: "map, enum, or struct"
381                                        });
382                                    }
383                                }
384
385                                stack.push(Instruction::ObjectKeyOrObjectClose)
386                            }
387                            Token::LBracket => {
388                                match wip.innermost_shape().def {
389                                    Def::Array(_) => {
390                                        trace!(
391                                            "Array starting for array ({})!",
392                                            wip.shape().blue()
393                                        );
394                                    }
395                                    Def::Slice(_) => {
396                                        trace!(
397                                            "Array starting for slice ({})!",
398                                            wip.shape().blue()
399                                        );
400                                    }
401                                    Def::List(_) => {
402                                        trace!("Array starting for list ({})!", wip.shape().blue());
403                                        reflect!(put_default());
404                                    }
405                                    _ => {
406                                        bail!(JsonErrorKind::UnsupportedType {
407                                            got: wip.innermost_shape(),
408                                            wanted: "array, list, or slice"
409                                        });
410                                    }
411                                }
412
413                                trace!("Beginning pushback");
414                                reflect!(begin_pushback());
415                                stack.push(Instruction::ArrayItemOrArrayClose)
416                            }
417                            Token::RBrace | Token::RBracket | Token::Colon | Token::Comma => {
418                                bail!(JsonErrorKind::UnexpectedToken {
419                                    got: token.node,
420                                    wanted: "value"
421                                });
422                            }
423                            Token::String(s) => match wip.innermost_shape().def {
424                                Def::Scalar(_sd) => {
425                                    reflect!(put::<String>(s));
426                                }
427                                Def::Enum(_ed) => {
428                                    if wip.selected_variant().is_some() {
429                                        // just put, then — if it's a tuple field it'll work
430                                        reflect!(put::<String>(s));
431                                    } else {
432                                        match wip.find_variant(&s) {
433                                            Some((variant_index, _)) => {
434                                                reflect!(variant(variant_index));
435                                            }
436                                            None => {
437                                                bail!(JsonErrorKind::NoSuchVariant {
438                                                    name: s.to_string(),
439                                                    enum_shape: wip.shape()
440                                                });
441                                            }
442                                        }
443                                    }
444                                }
445                                _ => bail!(JsonErrorKind::UnsupportedType {
446                                    got: wip.innermost_shape(),
447                                    wanted: "enum or string"
448                                }),
449                            },
450                            Token::F64(n) => {
451                                if wip.innermost_shape() == <f32 as Facet>::SHAPE {
452                                    reflect!(put(n as f32));
453                                } else {
454                                    reflect!(put(n));
455                                }
456                            }
457                            Token::U64(n) => {
458                                reflect!(put(n));
459                            }
460                            Token::I64(n) => {
461                                reflect!(put(n));
462                            }
463                            Token::True => {
464                                reflect!(put::<bool>(true));
465                            }
466                            Token::False => {
467                                reflect!(put::<bool>(false));
468                            }
469                            Token::EOF => {
470                                bail!(JsonErrorKind::UnexpectedEof("in value"));
471                            }
472                        }
473                    }
474                }
475            }
476            Instruction::ObjectKeyOrObjectClose => {
477                let token = read_token!();
478                match token.node {
479                    Token::String(key) => {
480                        trace!("Object key: {}", key);
481
482                        let mut ignore = false;
483                        let mut transparent = false;
484
485                        match wip.shape().def {
486                            Def::Struct(_) => match wip.field_index(&key) {
487                                Some(index) => {
488                                    reflect!(field(index));
489                                }
490                                None => {
491                                    if wip.shape().has_deny_unknown_fields_attr() {
492                                        // well, it all depends.
493                                        bail!(JsonErrorKind::UnknownField {
494                                            field_name: key.to_string(),
495                                            shape: wip.shape(),
496                                        })
497                                    } else {
498                                        trace!("Will ignore key ");
499                                        ignore = true;
500                                    }
501                                }
502                            },
503                            Def::Enum(_sd) => match wip.find_variant(&key) {
504                                Some((index, variant)) => {
505                                    trace!("Variant {} selected", variant.name.blue());
506                                    reflect!(variant(index));
507                                    transparent = true;
508                                }
509                                None => {
510                                    bail!(JsonErrorKind::NoSuchVariant {
511                                        name: key.to_string(),
512                                        enum_shape: wip.shape()
513                                    });
514                                }
515                            },
516                            Def::Map(_) => {
517                                reflect!(push_map_key());
518                                reflect!(put(key));
519                                reflect!(push_map_value());
520                            }
521                            _ => {
522                                bail!(JsonErrorKind::Unimplemented(
523                                    "object key for non-struct/map"
524                                ));
525                            }
526                        }
527
528                        let colon = read_token!();
529                        if colon.node != Token::Colon {
530                            bail!(JsonErrorKind::UnexpectedToken {
531                                got: colon.node,
532                                wanted: "colon"
533                            });
534                        }
535                        stack.push(Instruction::CommaThenObjectKeyOrObjectClose);
536                        if ignore {
537                            stack.push(Instruction::SkipValue);
538                        } else {
539                            if transparent {
540                                trace!(
541                                    "Transparent wrapper (like an outer-tagged enum), not pushing Pop insn to stack"
542                                )
543                            } else {
544                                stack.push(Instruction::Pop(PopReason::ObjectVal));
545                            }
546                            stack.push(Instruction::Value);
547                        }
548                    }
549                    Token::RBrace => {
550                        trace!("Object closing");
551                    }
552                    _ => {
553                        bail!(JsonErrorKind::UnexpectedToken {
554                            got: token.node,
555                            wanted: "object key or closing brace"
556                        });
557                    }
558                }
559            }
560            Instruction::CommaThenObjectKeyOrObjectClose => {
561                let token = read_token!();
562                match token.node {
563                    Token::Comma => {
564                        trace!("Object comma");
565                        stack.push(Instruction::ObjectKeyOrObjectClose);
566                    }
567                    Token::RBrace => {
568                        trace!("Object close");
569                    }
570                    _ => {
571                        bail!(JsonErrorKind::UnexpectedToken {
572                            got: token.node,
573                            wanted: "comma"
574                        });
575                    }
576                }
577            }
578            Instruction::ArrayItemOrArrayClose => {
579                let token = read_token!();
580                match token.node {
581                    Token::RBracket => {
582                        trace!("Array close");
583                    }
584                    _ => {
585                        trace!("Array item");
586                        put_back_token!(token);
587                        reflect!(begin_pushback());
588                        reflect!(push());
589
590                        stack.push(Instruction::CommaThenArrayItemOrArrayClose);
591                        stack.push(Instruction::Pop(PopReason::ArrayItem));
592                        stack.push(Instruction::Value);
593                    }
594                }
595            }
596            Instruction::CommaThenArrayItemOrArrayClose => {
597                let token = read_token!();
598                match token.node {
599                    Token::RBracket => {
600                        trace!("Array close");
601                    }
602                    Token::Comma => {
603                        trace!("Array comma");
604                        reflect!(push());
605                        stack.push(Instruction::CommaThenArrayItemOrArrayClose);
606                        stack.push(Instruction::Pop(PopReason::ArrayItem));
607                        stack.push(Instruction::Value);
608                    }
609                    _ => {
610                        bail!(JsonErrorKind::UnexpectedToken {
611                            got: token.node,
612                            wanted: "comma or closing bracket"
613                        });
614                    }
615                }
616            }
617        }
618    }
619}