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}