facet_reflect/
resolution.rs

1//! Resolution types for representing resolved type configurations.
2//!
3//! A [`Resolution`] represents one possible "shape" a type can take after
4//! all enum variants in flatten paths have been selected. It contains all
5//! the fields that exist in that configuration, along with their paths.
6
7extern crate alloc;
8
9use alloc::borrow::Cow;
10use alloc::collections::BTreeMap;
11use alloc::collections::BTreeSet;
12use alloc::format;
13use alloc::string::String;
14use alloc::string::ToString;
15use alloc::vec::Vec;
16use core::fmt;
17
18use facet_core::{Field, Shape};
19
20/// A path of serialized key names for probing.
21/// Unlike FieldPath which tracks the internal type structure (including variant selections),
22/// KeyPath only tracks the keys as they appear in the serialized format.
23pub type KeyPath = Vec<&'static str>;
24
25/// A segment in a field path.
26#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
27pub enum PathSegment {
28    /// A regular struct field
29    Field(&'static str),
30    /// An enum variant selection (field_name, variant_name)
31    Variant(&'static str, &'static str),
32}
33
34/// A path through the type tree to a field.
35#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
36pub struct FieldPath {
37    segments: Vec<PathSegment>,
38}
39
40impl fmt::Debug for FieldPath {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "FieldPath(")?;
43        for (i, seg) in self.segments.iter().enumerate() {
44            if i > 0 {
45                write!(f, ".")?;
46            }
47            match seg {
48                PathSegment::Field(name) => write!(f, "{name}")?,
49                PathSegment::Variant(field, variant) => write!(f, "{field}::{variant}")?,
50            }
51        }
52        write!(f, ")")
53    }
54}
55
56impl fmt::Display for FieldPath {
57    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
58        let mut first = true;
59        for seg in &self.segments {
60            match seg {
61                PathSegment::Field(name) => {
62                    if !first {
63                        write!(f, ".")?;
64                    }
65                    write!(f, "{name}")?;
66                    first = false;
67                }
68                PathSegment::Variant(_, _) => {
69                    // Skip variant segments in display path - they're internal
70                }
71            }
72        }
73        Ok(())
74    }
75}
76
77impl FieldPath {
78    /// Create an empty path (root level).
79    pub fn empty() -> Self {
80        Self {
81            segments: Vec::new(),
82        }
83    }
84
85    /// Get the depth of this path.
86    pub fn depth(&self) -> usize {
87        self.segments.len()
88    }
89
90    /// Push a field segment onto the path.
91    pub fn push_field(&self, name: &'static str) -> Self {
92        let mut new = self.clone();
93        new.segments.push(PathSegment::Field(name));
94        new
95    }
96
97    /// Push a variant segment onto the path.
98    pub fn push_variant(&self, field_name: &'static str, variant_name: &'static str) -> Self {
99        let mut new = self.clone();
100        new.segments
101            .push(PathSegment::Variant(field_name, variant_name));
102        new
103    }
104
105    /// Get the parent path (all segments except the last).
106    pub fn parent(&self) -> Self {
107        let mut new = self.clone();
108        new.segments.pop();
109        new
110    }
111
112    /// Get the segments of this path.
113    pub fn segments(&self) -> &[PathSegment] {
114        &self.segments
115    }
116
117    /// Get the last segment, if any.
118    pub fn last(&self) -> Option<&PathSegment> {
119        self.segments.last()
120    }
121}
122
123/// Records that a specific enum field has a specific variant selected.
124#[derive(Debug, Clone)]
125pub struct VariantSelection {
126    /// Path to the enum field from root
127    pub path: FieldPath,
128    /// Name of the enum type (e.g., "MessagePayload")
129    pub enum_name: &'static str,
130    /// Name of the selected variant (e.g., "Text")
131    pub variant_name: &'static str,
132}
133
134/// Information about a single field in a resolution.
135#[derive(Debug, Clone)]
136pub struct FieldInfo {
137    /// The name as it appears in the serialized format
138    pub serialized_name: &'static str,
139
140    /// Full path from root to this field
141    pub path: FieldPath,
142
143    /// Whether this field is required (not Option, no default)
144    pub required: bool,
145
146    /// The shape of this field's value
147    pub value_shape: &'static Shape,
148
149    /// The original field definition (for accessing flags, attributes, etc.)
150    pub field: &'static Field,
151}
152
153impl PartialEq for FieldInfo {
154    fn eq(&self, other: &Self) -> bool {
155        self.serialized_name == other.serialized_name
156            && self.path == other.path
157            && self.required == other.required
158            && core::ptr::eq(self.value_shape, other.value_shape)
159            && core::ptr::eq(self.field, other.field)
160    }
161}
162
163impl Eq for FieldInfo {}
164
165/// Result of matching input fields against a resolution.
166#[derive(Debug)]
167pub enum MatchResult {
168    /// All required fields present, all fields known
169    Exact,
170    /// All required fields present, some optional fields missing
171    WithOptionalMissing(Vec<&'static str>),
172    /// Does not match
173    NoMatch {
174        /// Required fields that are missing
175        missing_required: Vec<&'static str>,
176        /// Fields that are not known in this resolution
177        unknown: Vec<String>,
178    },
179}
180
181/// One possible "shape" the flattened type could take.
182///
183/// Represents a specific choice of variants for all enums in the flatten tree.
184/// This is the "resolution" of all ambiguity in the type — all enum variants
185/// have been selected, all fields are known.
186#[derive(Debug, Clone)]
187pub struct Resolution {
188    /// For each enum in the flatten path, which variant is selected.
189    /// The key is the path to the enum field, value is the variant.
190    variant_selections: Vec<VariantSelection>,
191
192    /// All fields in this configuration, keyed by serialized name.
193    fields: BTreeMap<&'static str, FieldInfo>,
194
195    /// Set of required field names (for quick matching)
196    required_field_names: BTreeSet<&'static str>,
197
198    /// All known key paths at all depths (for depth-aware probing).
199    /// Each path is a sequence of serialized key names from root.
200    /// E.g., for `{payload: {content: "hi"}}`, contains `["payload"]` and `["payload", "content"]`.
201    known_paths: BTreeSet<KeyPath>,
202}
203
204/// Error when building a resolution.
205#[derive(Debug, Clone)]
206pub struct DuplicateFieldError {
207    /// The duplicate field name
208    pub field_name: &'static str,
209    /// The first path where this field was found
210    pub first_path: FieldPath,
211    /// The second path where this field was found
212    pub second_path: FieldPath,
213}
214
215impl fmt::Display for DuplicateFieldError {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        write!(
218            f,
219            "duplicate field '{}': found at {} and {}",
220            self.field_name, self.first_path, self.second_path
221        )
222    }
223}
224
225impl Resolution {
226    /// Create a new empty resolution.
227    pub fn new() -> Self {
228        Self {
229            variant_selections: Vec::new(),
230            fields: BTreeMap::new(),
231            required_field_names: BTreeSet::new(),
232            known_paths: BTreeSet::new(),
233        }
234    }
235
236    /// Add a key path (for depth-aware probing).
237    pub fn add_key_path(&mut self, path: KeyPath) {
238        self.known_paths.insert(path);
239    }
240
241    /// Add a field to this resolution.
242    ///
243    /// Returns an error if a field with the same serialized name already exists
244    /// but comes from a different source (different path). This catches duplicate
245    /// field name conflicts between parent structs and flattened fields.
246    pub fn add_field(&mut self, info: FieldInfo) -> Result<(), DuplicateFieldError> {
247        if let Some(existing) = self.fields.get(info.serialized_name)
248            && existing.path != info.path
249        {
250            return Err(DuplicateFieldError {
251                field_name: info.serialized_name,
252                first_path: existing.path.clone(),
253                second_path: info.path,
254            });
255        }
256        if info.required {
257            self.required_field_names.insert(info.serialized_name);
258        }
259        self.fields.insert(info.serialized_name, info);
260        Ok(())
261    }
262
263    /// Add a variant selection to this resolution.
264    pub fn add_variant_selection(
265        &mut self,
266        path: FieldPath,
267        enum_name: &'static str,
268        variant_name: &'static str,
269    ) {
270        self.variant_selections.push(VariantSelection {
271            path,
272            enum_name,
273            variant_name,
274        });
275    }
276
277    /// Merge another resolution into this one.
278    ///
279    /// Returns an error if a field with the same serialized name already exists
280    /// but comes from a different source (different path). This catches duplicate
281    /// field name conflicts between parent structs and flattened fields.
282    pub fn merge(&mut self, other: &Resolution) -> Result<(), DuplicateFieldError> {
283        for (name, info) in &other.fields {
284            if let Some(existing) = self.fields.get(*name)
285                && existing.path != info.path
286            {
287                return Err(DuplicateFieldError {
288                    field_name: name,
289                    first_path: existing.path.clone(),
290                    second_path: info.path.clone(),
291                });
292            }
293            self.fields.insert(*name, info.clone());
294            if info.required {
295                self.required_field_names.insert(*name);
296            }
297        }
298        for vs in &other.variant_selections {
299            self.variant_selections.push(vs.clone());
300        }
301        for path in &other.known_paths {
302            self.known_paths.insert(path.clone());
303        }
304        Ok(())
305    }
306
307    /// Mark all fields as optional (required = false).
308    /// Used when a flattened field is wrapped in `Option<T>`.
309    pub fn mark_all_optional(&mut self) {
310        self.required_field_names.clear();
311        for info in self.fields.values_mut() {
312            info.required = false;
313        }
314    }
315
316    /// Check if this resolution matches the input fields.
317    pub fn matches(&self, input_fields: &BTreeSet<Cow<'_, str>>) -> MatchResult {
318        let mut missing_required = Vec::new();
319        let mut missing_optional = Vec::new();
320
321        for (name, info) in &self.fields {
322            if !input_fields.iter().any(|k| k.as_ref() == *name) {
323                if info.required {
324                    missing_required.push(*name);
325                } else {
326                    missing_optional.push(*name);
327                }
328            }
329        }
330
331        // Check for unknown fields
332        let unknown: Vec<String> = input_fields
333            .iter()
334            .filter(|f| !self.fields.contains_key(f.as_ref()))
335            .map(|s| s.to_string())
336            .collect();
337
338        if !missing_required.is_empty() || !unknown.is_empty() {
339            MatchResult::NoMatch {
340                missing_required,
341                unknown,
342            }
343        } else if missing_optional.is_empty() {
344            MatchResult::Exact
345        } else {
346            MatchResult::WithOptionalMissing(missing_optional)
347        }
348    }
349
350    /// Get a human-readable description of this resolution.
351    ///
352    /// Returns something like `MessagePayload::Text` or `Auth::Token + Transport::Tcp`
353    /// for resolutions with multiple variant selections.
354    pub fn describe(&self) -> String {
355        if self.variant_selections.is_empty() {
356            String::from("(no variants)")
357        } else {
358            let parts: Vec<_> = self
359                .variant_selections
360                .iter()
361                .map(|vs| format!("{}::{}", vs.enum_name, vs.variant_name))
362                .collect();
363            parts.join(" + ")
364        }
365    }
366
367    /// Get the fields in deserialization order (deepest first).
368    pub fn deserialization_order(&self) -> Vec<&FieldInfo> {
369        let mut fields: Vec<_> = self.fields.values().collect();
370        fields.sort_by(|a, b| {
371            // Deeper paths first
372            b.path
373                .depth()
374                .cmp(&a.path.depth())
375                // Then lexicographic for determinism
376                .then_with(|| a.path.cmp(&b.path))
377        });
378        fields
379    }
380
381    /// Get a field by name.
382    pub fn field(&self, name: &str) -> Option<&FieldInfo> {
383        self.fields.get(name)
384    }
385
386    /// Get all fields.
387    pub fn fields(&self) -> &BTreeMap<&'static str, FieldInfo> {
388        &self.fields
389    }
390
391    /// Get the set of required field names.
392    pub fn required_field_names(&self) -> &BTreeSet<&'static str> {
393        &self.required_field_names
394    }
395
396    /// Get optional fields that were NOT provided in the input.
397    ///
398    /// This is useful for deserializers that need to initialize missing
399    /// optional fields to `None` or their default value.
400    pub fn missing_optional_fields<'a>(
401        &'a self,
402        seen_keys: &'a BTreeSet<Cow<'_, str>>,
403    ) -> impl Iterator<Item = &'a FieldInfo> {
404        self.fields.values().filter(move |info| {
405            !info.required && !seen_keys.iter().any(|k| k.as_ref() == info.serialized_name)
406        })
407    }
408
409    /// Get variant selections.
410    pub fn variant_selections(&self) -> &[VariantSelection] {
411        &self.variant_selections
412    }
413
414    /// Get all child fields (fields with the CHILD flag).
415    ///
416    /// This is useful for KDL deserialization where child nodes need to be
417    /// processed separately from properties.
418    pub fn child_fields(&self) -> impl Iterator<Item = &FieldInfo> {
419        self.fields.values().filter(|f| f.field.is_child())
420    }
421
422    /// Get all property fields (fields without the child attribute).
423    ///
424    /// This is useful for KDL deserialization where properties are processed
425    /// separately from child nodes.
426    pub fn property_fields(&self) -> impl Iterator<Item = &FieldInfo> {
427        self.fields.values().filter(|f| !f.field.is_child())
428    }
429
430    /// Get all known key paths (for depth-aware probing).
431    pub fn known_paths(&self) -> &BTreeSet<KeyPath> {
432        &self.known_paths
433    }
434
435    /// Check if this resolution has a specific key path.
436    /// Compares runtime strings against static schema paths.
437    pub fn has_key_path(&self, path: &[&str]) -> bool {
438        self.known_paths.iter().any(|known| {
439            known.len() == path.len() && known.iter().zip(path.iter()).all(|(a, b)| *a == *b)
440        })
441    }
442}
443
444impl Default for Resolution {
445    fn default() -> Self {
446        Self::new()
447    }
448}