facet_json/
deserialize.rs

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