1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
extern crate alloc;
use crate::{FormatDeserializer, FormatParser, ScalarValue};
use facet_core::Def;
impl<'input, const BORROW: bool, P> FormatDeserializer<'input, BORROW, P>
where
P: FormatParser<'input>,
{
pub(crate) fn scalar_matches_shape(
&self,
scalar: &ScalarValue<'input>,
shape: &'static facet_core::Shape,
) -> bool {
use facet_core::ScalarType;
let Some(scalar_type) = shape.scalar_type() else {
// Not a scalar type - check for Option wrapping null
if matches!(scalar, ScalarValue::Null) {
return matches!(shape.def, Def::Option(_));
}
return false;
};
match scalar {
ScalarValue::Bool(_) => matches!(scalar_type, ScalarType::Bool),
ScalarValue::Char(_) => matches!(scalar_type, ScalarType::Char),
ScalarValue::I64(val) => {
// I64 matches signed types directly
if matches!(
scalar_type,
ScalarType::I8
| ScalarType::I16
| ScalarType::I32
| ScalarType::I64
| ScalarType::I128
| ScalarType::ISize
) {
return true;
}
// I64 can also match unsigned types if the value is non-negative and in range
// This handles TOML's requirement to represent all integers as i64
if *val >= 0 {
let uval = *val as u64;
match scalar_type {
ScalarType::U8 => uval <= u8::MAX as u64,
ScalarType::U16 => uval <= u16::MAX as u64,
ScalarType::U32 => uval <= u32::MAX as u64,
ScalarType::U64 | ScalarType::U128 | ScalarType::USize => true,
_ => false,
}
} else {
false
}
}
ScalarValue::U64(_) => matches!(
scalar_type,
ScalarType::U8
| ScalarType::U16
| ScalarType::U32
| ScalarType::U64
| ScalarType::U128
| ScalarType::USize
),
ScalarValue::U128(_) => matches!(scalar_type, ScalarType::U128 | ScalarType::I128),
ScalarValue::I128(_) => matches!(scalar_type, ScalarType::I128 | ScalarType::U128),
ScalarValue::F64(_) => matches!(scalar_type, ScalarType::F32 | ScalarType::F64),
ScalarValue::Str(s) => {
// String scalars match string types directly
if matches!(
scalar_type,
ScalarType::String | ScalarType::Str | ScalarType::CowStr | ScalarType::Char
) {
return true;
}
// For other scalar types, check if the shape has a parse function
// and if so, try parsing the string to see if it would succeed.
// This enables untagged enums to correctly match string values like "4.625"
// to the appropriate variant (f64 vs i64).
// See #1615 for discussion of this double-parse pattern.
#[allow(unsafe_code)]
if shape.vtable.has_parse()
&& shape
.layout
.sized_layout()
.is_ok_and(|layout| layout.size() <= 128)
{
// Attempt to parse - this is a probe, not the actual deserialization
let mut temp = [0u8; 128];
let temp_ptr = facet_core::PtrUninit::new(temp.as_mut_ptr());
// SAFETY: temp buffer is properly aligned and sized for this shape
if let Some(Ok(())) = unsafe { shape.call_parse(s.as_ref(), temp_ptr) } {
// Parse succeeded - drop the temp value
// SAFETY: we just successfully parsed into temp_ptr
unsafe { shape.call_drop_in_place(temp_ptr.assume_init()) };
return true;
}
}
false
}
ScalarValue::Bytes(_) => {
// Bytes don't have a ScalarType - would need to check for Vec<u8> or [u8]
false
}
ScalarValue::Null => {
// Null matches Unit type
matches!(scalar_type, ScalarType::Unit)
}
ScalarValue::Unit => {
// Unit matches Unit type
matches!(scalar_type, ScalarType::Unit)
}
}
}
}