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