facet_format/event.rs
1extern crate alloc;
2
3use alloc::borrow::Cow;
4use core::fmt;
5
6/// Location hint for a serialized field.
7#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub enum FieldLocationHint {
9 /// Key/value entry (JSON/YAML/TOML).
10 KeyValue,
11 /// XML attribute.
12 Attribute,
13 /// XML/KDL text node.
14 Text,
15 /// XML/KDL child element/node.
16 Child,
17 /// KDL property.
18 Property,
19 /// KDL positional argument.
20 Argument,
21 /// Element tag name (for custom elements in XML/HTML).
22 Tag,
23}
24
25/// Field key with optional namespace (for XML).
26#[derive(Debug, Clone, PartialEq, Eq)]
27pub struct FieldKey<'de> {
28 /// Field name.
29 pub name: Cow<'de, str>,
30 /// Location hint.
31 pub location: FieldLocationHint,
32 /// Optional namespace URI (for XML namespace support).
33 pub namespace: Option<Cow<'de, str>>,
34}
35
36impl<'de> FieldKey<'de> {
37 /// Create a new field key without namespace.
38 pub fn new(name: impl Into<Cow<'de, str>>, location: FieldLocationHint) -> Self {
39 Self {
40 name: name.into(),
41 location,
42 namespace: None,
43 }
44 }
45
46 /// Add a namespace to this field key (builder pattern).
47 pub fn with_namespace(mut self, namespace: impl Into<Cow<'de, str>>) -> Self {
48 self.namespace = Some(namespace.into());
49 self
50 }
51}
52
53/// The kind of container being parsed.
54///
55/// This distinguishes between format-specific container types to enable
56/// better error messages and type checking.
57#[derive(Debug, Clone, Copy, PartialEq, Eq)]
58pub enum ContainerKind {
59 /// JSON/YAML/TOML object: definitely struct-like with key-value pairs.
60 /// Type mismatches (e.g., object where array expected) should produce errors.
61 Object,
62 /// JSON/YAML array: definitely sequence-like.
63 /// Type mismatches (e.g., array where object expected) should produce errors.
64 Array,
65 /// XML/KDL element: semantically ambiguous.
66 /// Could be interpreted as struct, sequence, or scalar wrapper depending on target type.
67 /// The deserializer decides based on what type it's deserializing into.
68 Element,
69}
70
71impl ContainerKind {
72 /// Returns true if this container kind is ambiguous (can be struct or sequence).
73 pub fn is_ambiguous(self) -> bool {
74 matches!(self, ContainerKind::Element)
75 }
76
77 /// Human-readable name for error messages.
78 pub fn name(self) -> &'static str {
79 match self {
80 ContainerKind::Object => "object",
81 ContainerKind::Array => "array",
82 ContainerKind::Element => "element",
83 }
84 }
85}
86
87/// Value classification hint for evidence gathering.
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89pub enum ValueTypeHint {
90 /// Null-like values.
91 Null,
92 /// Boolean.
93 Bool,
94 /// Numeric primitive.
95 Number,
96 /// Text string.
97 String,
98 /// Raw bytes (e.g., base64 segments).
99 Bytes,
100 /// Sequence (array/list/tuple).
101 Sequence,
102 /// Map/struct/object.
103 Map,
104}
105
106/// Scalar data extracted from the wire format.
107#[derive(Debug, Clone, PartialEq)]
108pub enum ScalarValue<'de> {
109 /// Null literal.
110 Null,
111 /// Boolean literal.
112 Bool(bool),
113 /// Character literal.
114 Char(char),
115 /// Signed integer literal (fits in i64).
116 I64(i64),
117 /// Unsigned integer literal (fits in u64).
118 U64(u64),
119 /// Signed 128-bit integer literal.
120 I128(i128),
121 /// Unsigned 128-bit integer literal.
122 U128(u128),
123 /// Floating-point literal.
124 F64(f64),
125 /// UTF-8 string literal (definitely a string, not a number).
126 Str(Cow<'de, str>),
127 /// Binary literal.
128 Bytes(Cow<'de, [u8]>),
129 /// Stringly-typed value from formats like XML where all values are text.
130 ///
131 /// Unlike `Str`, this value's type is ambiguous - it could be a number,
132 /// boolean, or actual string depending on the target type. The deserializer
133 /// will attempt to parse it according to the expected type.
134 ///
135 /// Examples:
136 /// - XML `<value>42</value>` → StringlyTyped("42") → parses as i32, u64, String, etc.
137 /// - XML `<value>2.5</value>` → StringlyTyped("2.5") → parses as f64, Decimal, String, etc.
138 /// - JSON `"42"` → Str("42") → definitely a string, not a number
139 StringlyTyped(Cow<'de, str>),
140}
141
142/// Event emitted by a format parser while streaming through input.
143#[derive(Clone, PartialEq)]
144pub enum ParseEvent<'de> {
145 /// Beginning of a struct/object/node.
146 StructStart(ContainerKind),
147 /// End of a struct/object/node.
148 StructEnd,
149 /// Encountered a field key (for self-describing formats like JSON/YAML).
150 FieldKey(FieldKey<'de>),
151 /// Next field value in struct field order (for non-self-describing formats like postcard).
152 ///
153 /// The driver tracks the current field index and uses the schema to determine
154 /// which field this value belongs to. This allows formats without field names
155 /// in the wire format to still support Tier-0 deserialization.
156 OrderedField,
157 /// Beginning of a sequence/array/tuple.
158 SequenceStart(ContainerKind),
159 /// End of a sequence/array/tuple.
160 SequenceEnd,
161 /// Scalar literal.
162 Scalar(ScalarValue<'de>),
163 /// Variant discriminant that needs to be propagated to the solver.
164 VariantTag(&'de str),
165}
166
167impl<'de> fmt::Debug for ParseEvent<'de> {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 ParseEvent::StructStart(kind) => f.debug_tuple("StructStart").field(kind).finish(),
171 ParseEvent::StructEnd => f.write_str("StructEnd"),
172 ParseEvent::FieldKey(key) => f.debug_tuple("FieldKey").field(key).finish(),
173 ParseEvent::OrderedField => f.write_str("OrderedField"),
174 ParseEvent::SequenceStart(kind) => f.debug_tuple("SequenceStart").field(kind).finish(),
175 ParseEvent::SequenceEnd => f.write_str("SequenceEnd"),
176 ParseEvent::Scalar(value) => f.debug_tuple("Scalar").field(value).finish(),
177 ParseEvent::VariantTag(tag) => f.debug_tuple("VariantTag").field(tag).finish(),
178 }
179 }
180}