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}