facet_solver/
lib.rs

1#![cfg_attr(not(feature = "std"), no_std)]
2#![doc = include_str!("../README.md")]
3
4extern crate alloc;
5
6use alloc::collections::BTreeMap;
7use alloc::collections::BTreeSet;
8use alloc::string::{String, ToString};
9use alloc::vec;
10use alloc::vec::Vec;
11use core::fmt;
12
13use facet_core::{Def, Field, FieldFlags, Shape, StructType, Type, UserType, Variant};
14
15/// Cached schema for a type that may contain flattened fields.
16///
17/// This is computed once per Shape and can be cached forever since
18/// type information is static.
19#[derive(Debug)]
20pub struct Schema {
21    /// The shape this schema is for (kept for future caching key)
22    #[allow(dead_code)]
23    shape: &'static Shape,
24
25    /// All possible configurations of this type.
26    /// For types with no enums in flatten paths, this has exactly 1 entry.
27    /// For types with enums, this has one entry per valid combination of variants.
28    configurations: Vec<Configuration>,
29
30    /// Inverted index: field_name → bitmask of configuration indices.
31    /// Bit i is set if `configurations[i]` contains this field.
32    /// Uses a `Vec<u64>` to support arbitrary numbers of configurations.
33    field_to_configs: BTreeMap<&'static str, ConfigSet>,
34}
35
36/// A set of configuration indices, stored as a bitmask for O(1) intersection.
37#[derive(Debug, Clone, PartialEq, Eq)]
38pub struct ConfigSet {
39    /// Bitmask where bit i indicates `configurations[i]` is in the set.
40    /// For most types, a single u64 suffices (up to 64 configs).
41    bits: Vec<u64>,
42    /// Number of configurations in the set.
43    count: usize,
44}
45
46impl ConfigSet {
47    /// Create an empty config set.
48    fn empty(num_configs: usize) -> Self {
49        let num_words = num_configs.div_ceil(64);
50        Self {
51            bits: vec![0; num_words],
52            count: 0,
53        }
54    }
55
56    /// Create a full config set (all configs present).
57    fn full(num_configs: usize) -> Self {
58        let num_words = num_configs.div_ceil(64);
59        let mut bits = vec![!0u64; num_words];
60        // Clear bits beyond num_configs
61        if num_configs % 64 != 0 {
62            let last_word_bits = num_configs % 64;
63            bits[num_words - 1] = (1u64 << last_word_bits) - 1;
64        }
65        Self {
66            bits,
67            count: num_configs,
68        }
69    }
70
71    /// Insert a configuration index.
72    fn insert(&mut self, idx: usize) {
73        let word = idx / 64;
74        let bit = idx % 64;
75        if self.bits[word] & (1u64 << bit) == 0 {
76            self.bits[word] |= 1u64 << bit;
77            self.count += 1;
78        }
79    }
80
81    /// Intersect with another config set in place.
82    fn intersect_with(&mut self, other: &ConfigSet) {
83        self.count = 0;
84        for (a, b) in self.bits.iter_mut().zip(other.bits.iter()) {
85            *a &= *b;
86            self.count += a.count_ones() as usize;
87        }
88    }
89
90    /// Get the number of configurations in the set.
91    fn len(&self) -> usize {
92        self.count
93    }
94
95    /// Check if empty.
96    fn is_empty(&self) -> bool {
97        self.count == 0
98    }
99
100    /// Get the first (lowest) configuration index in the set.
101    fn first(&self) -> Option<usize> {
102        for (word_idx, &word) in self.bits.iter().enumerate() {
103            if word != 0 {
104                return Some(word_idx * 64 + word.trailing_zeros() as usize);
105            }
106        }
107        None
108    }
109
110    /// Iterate over configuration indices in the set.
111    fn iter(&self) -> impl Iterator<Item = usize> + '_ {
112        self.bits.iter().enumerate().flat_map(|(word_idx, &word)| {
113            (0..64).filter_map(move |bit| {
114                if word & (1u64 << bit) != 0 {
115                    Some(word_idx * 64 + bit)
116                } else {
117                    None
118                }
119            })
120        })
121    }
122}
123
124/// One possible "shape" the flattened type could take.
125///
126/// Represents a specific choice of variants for all enums in the flatten tree.
127#[derive(Debug, Clone)]
128pub struct Configuration {
129    /// For each enum in the flatten path, which variant is selected.
130    /// The key is the path to the enum field, value is the variant.
131    variant_selections: Vec<VariantSelection>,
132
133    /// All fields in this configuration, keyed by serialized name.
134    fields: BTreeMap<&'static str, FieldInfo>,
135
136    /// Set of required field names (for quick matching)
137    required_field_names: BTreeSet<&'static str>,
138
139    /// All known key paths at all depths (for depth-aware probing).
140    /// Each path is a sequence of serialized key names from root.
141    /// E.g., for `{payload: {content: "hi"}}`, contains `["payload"]` and `["payload", "content"]`.
142    known_paths: BTreeSet<KeyPath>,
143}
144
145/// A path of serialized key names for probing.
146/// Unlike FieldPath which tracks the internal type structure (including variant selections),
147/// KeyPath only tracks the keys as they appear in the serialized format.
148pub type KeyPath = Vec<&'static str>;
149
150/// Records that a specific enum field has a specific variant selected.
151#[derive(Debug, Clone)]
152pub struct VariantSelection {
153    /// Path to the enum field from root
154    pub path: FieldPath,
155    /// Name of the enum type (e.g., "MessagePayload")
156    pub enum_name: &'static str,
157    /// Name of the selected variant (e.g., "Text")
158    pub variant_name: &'static str,
159}
160
161/// Information about a single field in a configuration.
162#[derive(Debug, Clone, PartialEq, Eq)]
163pub struct FieldInfo {
164    /// The name as it appears in the serialized format
165    pub serialized_name: &'static str,
166
167    /// Full path from root to this field
168    pub path: FieldPath,
169
170    /// Whether this field is required (not Option, no default)
171    pub required: bool,
172
173    /// The shape of this field's value
174    pub value_shape: &'static Shape,
175}
176
177/// A path through the type tree to a field.
178#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
179pub struct FieldPath {
180    segments: Vec<PathSegment>,
181}
182
183impl fmt::Debug for FieldPath {
184    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
185        write!(f, "FieldPath(")?;
186        for (i, seg) in self.segments.iter().enumerate() {
187            if i > 0 {
188                write!(f, ".")?;
189            }
190            match seg {
191                PathSegment::Field(name) => write!(f, "{name}")?,
192                PathSegment::Variant(field, variant) => write!(f, "{field}::{variant}")?,
193            }
194        }
195        write!(f, ")")
196    }
197}
198
199impl fmt::Display for FieldPath {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        let mut first = true;
202        for seg in &self.segments {
203            match seg {
204                PathSegment::Field(name) => {
205                    if !first {
206                        write!(f, ".")?;
207                    }
208                    write!(f, "{name}")?;
209                    first = false;
210                }
211                PathSegment::Variant(_, _) => {
212                    // Skip variant segments in display path - they're internal
213                }
214            }
215        }
216        Ok(())
217    }
218}
219
220/// A segment in a field path.
221#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
222pub enum PathSegment {
223    /// A regular struct field
224    Field(&'static str),
225    /// An enum variant selection (field_name, variant_name)
226    Variant(&'static str, &'static str),
227}
228
229impl FieldPath {
230    /// Create an empty path (root level).
231    pub fn empty() -> Self {
232        Self {
233            segments: Vec::new(),
234        }
235    }
236
237    /// Get the depth of this path.
238    pub fn depth(&self) -> usize {
239        self.segments.len()
240    }
241
242    /// Push a field segment onto the path.
243    pub fn push_field(&self, name: &'static str) -> Self {
244        let mut new = self.clone();
245        new.segments.push(PathSegment::Field(name));
246        new
247    }
248
249    /// Push a variant segment onto the path.
250    pub fn push_variant(&self, field_name: &'static str, variant_name: &'static str) -> Self {
251        let mut new = self.clone();
252        new.segments
253            .push(PathSegment::Variant(field_name, variant_name));
254        new
255    }
256
257    /// Get the parent path (all segments except the last).
258    pub fn parent(&self) -> Self {
259        let mut new = self.clone();
260        new.segments.pop();
261        new
262    }
263
264    /// Get the segments of this path.
265    pub fn segments(&self) -> &[PathSegment] {
266        &self.segments
267    }
268
269    /// Get the last segment, if any.
270    pub fn last(&self) -> Option<&PathSegment> {
271        self.segments.last()
272    }
273}
274
275/// Result of matching input fields against a configuration.
276#[derive(Debug)]
277pub enum MatchResult {
278    /// All required fields present, all fields known
279    Exact,
280    /// All required fields present, some optional fields missing
281    WithOptionalMissing(Vec<&'static str>),
282    /// Does not match
283    NoMatch {
284        missing_required: Vec<&'static str>,
285        unknown: Vec<String>,
286    },
287}
288
289// ============================================================================
290// Incremental Solver
291// ============================================================================
292
293/// Incremental constraint solver for streaming deserialization.
294///
295/// Unlike batch solving where all field names are known upfront, this solver
296/// processes fields one at a time as they arrive. It minimizes deferred fields by
297/// immediately setting fields that have the same path in all remaining
298/// candidate configurations.
299///
300/// # Example
301///
302/// ```rust
303/// use facet::Facet;
304/// use facet_solver::{Schema, IncrementalSolver, FieldDecision};
305///
306/// #[derive(Facet)]
307/// struct SimpleConfig { port: u16 }
308///
309/// #[derive(Facet)]
310/// struct AdvancedConfig { host: String, port: u16 }
311///
312/// #[derive(Facet)]
313/// #[repr(u8)]
314/// enum Config {
315///     Simple(SimpleConfig),
316///     Advanced(AdvancedConfig),
317/// }
318///
319/// #[derive(Facet)]
320/// struct Server {
321///     name: String,
322///     #[facet(flatten)]
323///     config: Config,
324/// }
325///
326/// let schema = Schema::build(Server::SHAPE).unwrap();
327/// let mut solver = IncrementalSolver::new(&schema);
328///
329/// // "name" exists in all configs at the same path - set directly
330/// assert!(matches!(solver.see_key("name"), FieldDecision::SetDirectly(_)));
331///
332/// // "host" only exists in Advanced - disambiguates!
333/// match solver.see_key("host") {
334///     FieldDecision::Disambiguated { config, current_field } => {
335///         assert_eq!(current_field.serialized_name, "host");
336///         assert!(config.field("port").is_some()); // Advanced has port too
337///     }
338///     _ => panic!("expected disambiguation"),
339/// }
340/// ```
341#[derive(Debug)]
342pub struct IncrementalSolver<'a> {
343    /// Reference to the schema for configuration lookup
344    schema: &'a Schema,
345    /// Bitmask of remaining candidate configuration indices
346    candidates: ConfigSet,
347    /// Keys that have been deferred (for tracking purposes)
348    deferred_keys: Vec<&'a str>,
349}
350
351/// Result of presenting a key to the incremental solver.
352#[derive(Debug)]
353pub enum FieldDecision<'a> {
354    /// Field has the same path in all remaining candidates.
355    /// Set it directly - path is unambiguous.
356    SetDirectly(&'a FieldInfo),
357
358    /// Field exists in candidates but at different paths.
359    /// Defer (mark position for replay after disambiguation).
360    Defer,
361
362    /// This field narrowed candidates to exactly one.
363    /// Replay any deferred fields, then set this field.
364    Disambiguated {
365        /// The winning configuration
366        config: &'a Configuration,
367        /// Info for the field that caused disambiguation
368        current_field: &'a FieldInfo,
369    },
370
371    /// Field not found in any remaining candidate.
372    Unknown,
373}
374
375impl<'a> IncrementalSolver<'a> {
376    /// Create a new incremental solver from a schema.
377    pub fn new(schema: &'a Schema) -> Self {
378        Self {
379            schema,
380            candidates: ConfigSet::full(schema.configurations.len()),
381            deferred_keys: Vec::new(),
382        }
383    }
384
385    /// Get the current candidate configurations.
386    pub fn candidates(&self) -> Vec<&'a Configuration> {
387        self.candidates
388            .iter()
389            .map(|idx| &self.schema.configurations[idx])
390            .collect()
391    }
392
393    /// Process a field key. Returns what to do with the value.
394    pub fn see_key(&mut self, key: &'a str) -> FieldDecision<'a> {
395        // Use inverted index to find configs with this key - O(1) lookup!
396        let configs_with_key = match self.schema.field_to_configs.get(key) {
397            Some(set) => set,
398            None => return FieldDecision::Unknown,
399        };
400
401        // Intersect with current candidates - O(num_words) = O(configs/64)
402        let prev_count = self.candidates.len();
403        self.candidates.intersect_with(configs_with_key);
404
405        if self.candidates.is_empty() {
406            return FieldDecision::Unknown;
407        }
408
409        // Did this narrow our candidates?
410        let narrowed = self.candidates.len() < prev_count;
411
412        // Check if we've disambiguated to exactly one (but only if we actually narrowed)
413        if narrowed && self.candidates.len() == 1 {
414            let idx = self.candidates.first().unwrap();
415            let config = &self.schema.configurations[idx];
416            let current_field = config.field(key).unwrap();
417            return FieldDecision::Disambiguated {
418                config,
419                current_field,
420            };
421        }
422
423        // Check if path is the same in all remaining candidates
424        let mut paths = BTreeSet::new();
425        for idx in self.candidates.iter() {
426            let config = &self.schema.configurations[idx];
427            if let Some(info) = config.field(key) {
428                paths.insert(&info.path);
429            }
430        }
431
432        if paths.len() == 1 {
433            // Same path in all candidates - can set directly
434            let idx = self.candidates.first().unwrap();
435            FieldDecision::SetDirectly(self.schema.configurations[idx].field(key).unwrap())
436        } else {
437            // Different paths - defer until disambiguation
438            self.deferred_keys.push(key);
439            FieldDecision::Defer
440        }
441    }
442
443    /// Get the keys that have been deferred.
444    pub fn deferred_keys(&self) -> &[&'a str] {
445        &self.deferred_keys
446    }
447
448    /// Clear the deferred keys (call after replaying).
449    pub fn clear_deferred(&mut self) {
450        self.deferred_keys.clear();
451    }
452
453    /// Finish solving. Returns error if still ambiguous or missing required fields.
454    ///
455    /// When multiple candidates remain, this filters out any that are missing
456    /// required fields. This handles the case where one variant's fields are
457    /// a subset of another's (e.g., `Http { url }` vs `Git { url, branch }`).
458    /// If input only contains `url`, both match the "has this field" check,
459    /// but `Git` is filtered out because it's missing required `branch`.
460    pub fn finish(self, seen_keys: &BTreeSet<&str>) -> Result<&'a Configuration, SolverError> {
461        if self.candidates.is_empty() {
462            return Err(SolverError::NoMatch {
463                input_fields: seen_keys.iter().map(|s| (*s).into()).collect(),
464                missing_required: Vec::new(),
465                missing_required_detailed: Vec::new(),
466                unknown_fields: Vec::new(),
467                closest_config: None,
468            });
469        }
470
471        // Filter candidates to only those that have all required fields satisfied
472        let satisfied: Vec<usize> = self
473            .candidates
474            .iter()
475            .filter(|idx| {
476                let config = &self.schema.configurations[*idx];
477                config
478                    .required_field_names
479                    .iter()
480                    .all(|f| seen_keys.contains(f))
481            })
482            .collect();
483
484        match satisfied.len() {
485            0 => {
486                // No config has all required fields satisfied.
487                // Find the "closest" match for a helpful error message.
488                let first_idx = self.candidates.first().unwrap();
489                let first_config = &self.schema.configurations[first_idx];
490                let missing: Vec<_> = first_config
491                    .required_field_names
492                    .iter()
493                    .filter(|f| !seen_keys.contains(*f))
494                    .copied()
495                    .collect();
496                let missing_detailed: Vec<_> = missing
497                    .iter()
498                    .filter_map(|name| first_config.field(name))
499                    .map(MissingFieldInfo::from_field_info)
500                    .collect();
501                Err(SolverError::NoMatch {
502                    input_fields: seen_keys.iter().map(|s| (*s).into()).collect(),
503                    missing_required: missing,
504                    missing_required_detailed: missing_detailed,
505                    unknown_fields: Vec::new(),
506                    closest_config: Some(first_config.describe()),
507                })
508            }
509            1 => {
510                let idx = satisfied[0];
511                Ok(&self.schema.configurations[idx])
512            }
513            _ => {
514                // Multiple configs still match after filtering - true ambiguity
515                // Find fields that could disambiguate (unique to only some configs)
516                let disambiguating = find_disambiguating_fields(
517                    &satisfied
518                        .iter()
519                        .map(|i| &self.schema.configurations[*i])
520                        .collect::<Vec<_>>(),
521                );
522                Err(SolverError::Ambiguous {
523                    candidates: satisfied
524                        .iter()
525                        .map(|idx| self.schema.configurations[*idx].describe())
526                        .collect(),
527                    disambiguating_fields: disambiguating,
528                })
529            }
530        }
531    }
532}
533
534/// Find fields that could disambiguate between configurations.
535/// Returns fields that exist in some but not all configurations.
536fn find_disambiguating_fields(configs: &[&Configuration]) -> Vec<String> {
537    if configs.len() < 2 {
538        return Vec::new();
539    }
540
541    // Collect all field names across all configs
542    let mut all_fields: BTreeSet<&str> = BTreeSet::new();
543    for config in configs {
544        for name in config.fields.keys() {
545            all_fields.insert(name);
546        }
547    }
548
549    // Find fields that are in some but not all configs
550    let mut disambiguating = Vec::new();
551    for field in all_fields {
552        let count = configs.iter().filter(|c| c.field(field).is_some()).count();
553        if count > 0 && count < configs.len() {
554            disambiguating.push(field.to_string());
555        }
556    }
557
558    disambiguating
559}
560
561/// Information about a missing required field for error reporting.
562#[derive(Debug, Clone)]
563pub struct MissingFieldInfo {
564    /// The serialized field name (as it appears in input)
565    pub name: &'static str,
566    /// Full path to the field (e.g., "backend.connection.port")
567    pub path: String,
568    /// The Rust type that defines this field
569    pub defined_in: String,
570}
571
572impl MissingFieldInfo {
573    /// Create from a FieldInfo
574    fn from_field_info(info: &FieldInfo) -> Self {
575        Self {
576            name: info.serialized_name,
577            path: info.path.to_string(),
578            defined_in: info.value_shape.type_identifier.to_string(),
579        }
580    }
581}
582
583/// Errors that can occur when building a schema.
584#[derive(Debug, Clone)]
585pub enum SchemaError {
586    /// A field name appears from multiple sources (parent struct and flattened struct)
587    DuplicateField {
588        /// The conflicting field name
589        field_name: &'static str,
590        /// Path to the first occurrence
591        first_path: FieldPath,
592        /// Path to the second occurrence
593        second_path: FieldPath,
594    },
595}
596
597impl fmt::Display for SchemaError {
598    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
599        match self {
600            SchemaError::DuplicateField {
601                field_name,
602                first_path,
603                second_path,
604            } => {
605                write!(
606                    f,
607                    "Duplicate field name '{field_name}' from different sources: {first_path} vs {second_path}. \
608                     This usually means a parent struct and a flattened struct both \
609                     define a field with the same name."
610                )
611            }
612        }
613    }
614}
615
616#[cfg(feature = "std")]
617impl std::error::Error for SchemaError {}
618
619/// Errors that can occur during flatten resolution.
620#[derive(Debug)]
621pub enum SolverError {
622    /// No configuration matches the input fields
623    NoMatch {
624        /// The input fields that were provided
625        input_fields: Vec<String>,
626        /// Missing required fields (from the closest matching config) - simple names for backwards compat
627        missing_required: Vec<&'static str>,
628        /// Missing required fields with full path information
629        missing_required_detailed: Vec<MissingFieldInfo>,
630        /// Unknown fields that don't belong to any config
631        unknown_fields: Vec<String>,
632        /// Description of the closest matching configuration
633        closest_config: Option<String>,
634    },
635    /// Multiple configurations match the input fields
636    Ambiguous {
637        /// Descriptions of the matching configurations
638        candidates: Vec<String>,
639        /// Fields that could disambiguate (unique to specific configs)
640        disambiguating_fields: Vec<String>,
641    },
642}
643
644impl fmt::Display for SolverError {
645    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
646        match self {
647            SolverError::NoMatch {
648                input_fields,
649                missing_required: _,
650                missing_required_detailed,
651                unknown_fields,
652                closest_config,
653            } => {
654                write!(f, "No matching configuration for fields {input_fields:?}")?;
655                if let Some(config) = closest_config {
656                    write!(f, " (closest match: {config})")?;
657                }
658                if !missing_required_detailed.is_empty() {
659                    write!(f, "; missing required fields:")?;
660                    for info in missing_required_detailed {
661                        write!(f, " {} (at path: {})", info.name, info.path)?;
662                    }
663                }
664                if !unknown_fields.is_empty() {
665                    write!(f, "; unknown: {unknown_fields:?}")?;
666                }
667                Ok(())
668            }
669            SolverError::Ambiguous {
670                candidates,
671                disambiguating_fields,
672            } => {
673                write!(
674                    f,
675                    "Ambiguous: multiple configurations match: {candidates:?}"
676                )?;
677                if !disambiguating_fields.is_empty() {
678                    write!(
679                        f,
680                        "; try adding one of these fields to disambiguate: {disambiguating_fields:?}"
681                    )?;
682                }
683                Ok(())
684            }
685        }
686    }
687}
688
689#[cfg(feature = "std")]
690impl std::error::Error for SolverError {}
691
692/// Compute a specificity score for a shape. Lower score = more specific.
693///
694/// This is used to disambiguate when a value could satisfy multiple types.
695/// For example, the value `42` fits both `u8` and `u16`, but `u8` is more
696/// specific (lower score), so it should be preferred.
697fn specificity_score(shape: &'static Shape) -> u64 {
698    // Use type_identifier to determine specificity
699    // Smaller integer types are more specific
700    match shape.type_identifier {
701        "u8" | "i8" => 8,
702        "u16" | "i16" => 16,
703        "u32" | "i32" | "f32" => 32,
704        "u64" | "i64" | "f64" => 64,
705        "u128" | "i128" => 128,
706        "usize" | "isize" => 64, // Treat as 64-bit
707        // Other types get a high score (less specific)
708        _ => 1000,
709    }
710}
711
712// ============================================================================
713// Solver (State Machine)
714// ============================================================================
715
716/// Result of reporting a key to the solver.
717#[derive(Debug)]
718pub enum KeyResult<'a> {
719    /// All candidates have the same type for this key.
720    /// The deserializer can parse the value directly.
721    Unambiguous {
722        /// The shape all candidates expect for this field
723        shape: &'static Shape,
724    },
725
726    /// Candidates have different types for this key - need disambiguation.
727    /// Deserializer should parse the value, determine which fields it can
728    /// satisfy, and call `satisfy()` with the viable fields.
729    ///
730    /// **Important**: When multiple fields can be satisfied by the value,
731    /// pick the one with the lowest score (most specific). Scores are assigned
732    /// by specificity, e.g., `u8` has a lower score than `u16`.
733    Ambiguous {
734        /// The unique fields across remaining candidates (deduplicated by shape),
735        /// paired with a specificity score. Lower score = more specific type.
736        /// Deserializer should check which of these the value can satisfy,
737        /// then pick the one with the lowest score.
738        fields: Vec<(&'a FieldInfo, u64)>,
739    },
740
741    /// This key disambiguated to exactly one configuration.
742    Solved(&'a Configuration),
743
744    /// This key doesn't exist in any remaining candidate.
745    Unknown,
746}
747
748/// Result of reporting which fields the value can satisfy.
749#[derive(Debug)]
750pub enum SatisfyResult<'a> {
751    /// Continue - still multiple candidates, keep feeding keys.
752    Continue,
753
754    /// Solved to exactly one configuration.
755    Solved(&'a Configuration),
756
757    /// No configuration can accept the value (no fields were satisfied).
758    NoMatch,
759}
760
761/// State machine solver for lazy value-based disambiguation.
762///
763/// This solver only requests value inspection when candidates disagree on type.
764/// For keys where all candidates expect the same type, the deserializer can
765/// skip detailed value analysis.
766///
767/// # Example
768///
769/// ```rust
770/// use facet::Facet;
771/// use facet_solver::{Schema, Solver, KeyResult, SatisfyResult};
772///
773/// #[derive(Facet)]
774/// #[repr(u8)]
775/// enum NumericValue {
776///     Small(u8),
777///     Large(u16),
778/// }
779///
780/// #[derive(Facet)]
781/// struct Container {
782///     #[facet(flatten)]
783///     value: NumericValue,
784/// }
785///
786/// let schema = Schema::build(Container::SHAPE).unwrap();
787/// let mut solver = Solver::new(&schema);
788///
789/// // The field "0" has different types (u8 vs u16) - solver needs disambiguation
790/// match solver.see_key("0") {
791///     KeyResult::Ambiguous { fields } => {
792///         // Deserializer sees value "1000", checks which fields can accept it
793///         // u8 can't hold 1000, u16 can - so only report the u16 field
794///         // Fields come with specificity scores - lower = more specific
795///         let satisfied: Vec<_> = fields.iter()
796///             .filter(|(f, _score)| {
797///                 // deserializer's logic: can this value parse as this field's type?
798///                 f.value_shape.type_identifier == "u16"
799///             })
800///             .map(|(f, _)| *f)
801///             .collect();
802///
803///         match solver.satisfy(&satisfied) {
804///             SatisfyResult::Solved(config) => {
805///                 assert!(config.describe().contains("Large"));
806///             }
807///             _ => panic!("expected solved"),
808///         }
809///     }
810///     _ => panic!("expected Ambiguous"),
811/// }
812/// ```
813#[derive(Debug)]
814pub struct Solver<'a> {
815    /// Reference to the schema for configuration lookup
816    schema: &'a Schema,
817    /// Bitmask of remaining candidate configuration indices
818    candidates: ConfigSet,
819    /// Set of seen keys for required field checking
820    seen_keys: BTreeSet<&'a str>,
821}
822
823impl<'a> Solver<'a> {
824    /// Create a new solver from a schema.
825    pub fn new(schema: &'a Schema) -> Self {
826        Self {
827            schema,
828            candidates: ConfigSet::full(schema.configurations.len()),
829            seen_keys: BTreeSet::new(),
830        }
831    }
832
833    /// Report a key. Returns what to do next.
834    ///
835    /// - `Unambiguous`: All candidates agree on the type - parse directly
836    /// - `Ambiguous`: Types differ - check which fields the value can satisfy
837    /// - `Solved`: Disambiguated to one config
838    /// - `Unknown`: Key not found in any candidate
839    pub fn see_key(&mut self, key: &'a str) -> KeyResult<'a> {
840        self.seen_keys.insert(key);
841
842        // Key-based filtering
843        let configs_with_key = match self.schema.field_to_configs.get(key) {
844            Some(set) => set,
845            None => return KeyResult::Unknown,
846        };
847
848        self.candidates.intersect_with(configs_with_key);
849
850        if self.candidates.is_empty() {
851            return KeyResult::Unknown;
852        }
853
854        // Check if we've disambiguated to exactly one
855        if self.candidates.len() == 1 {
856            let idx = self.candidates.first().unwrap();
857            return KeyResult::Solved(&self.schema.configurations[idx]);
858        }
859
860        // Collect unique fields (by shape pointer) across remaining candidates
861        let mut unique_fields: Vec<&'a FieldInfo> = Vec::new();
862        for idx in self.candidates.iter() {
863            let config = &self.schema.configurations[idx];
864            if let Some(info) = config.field(key) {
865                // Deduplicate by shape pointer
866                if !unique_fields
867                    .iter()
868                    .any(|f| core::ptr::eq(f.value_shape, info.value_shape))
869                {
870                    unique_fields.push(info);
871                }
872            }
873        }
874
875        if unique_fields.len() == 1 {
876            // All candidates have the same type - unambiguous
877            KeyResult::Unambiguous {
878                shape: unique_fields[0].value_shape,
879            }
880        } else {
881            // Different types - need disambiguation
882            // Attach specificity scores so caller can pick most specific when multiple match
883            let fields_with_scores: Vec<_> = unique_fields
884                .into_iter()
885                .map(|f| (f, specificity_score(f.value_shape)))
886                .collect();
887            KeyResult::Ambiguous {
888                fields: fields_with_scores,
889            }
890        }
891    }
892
893    /// Report which fields the value can satisfy after `Ambiguous` result.
894    ///
895    /// The deserializer should pass the subset of fields (from the `Ambiguous` result)
896    /// that the actual value can be parsed into.
897    pub fn satisfy(&mut self, satisfied_fields: &[&FieldInfo]) -> SatisfyResult<'a> {
898        let satisfied_shapes: Vec<_> = satisfied_fields.iter().map(|f| f.value_shape).collect();
899        self.satisfy_shapes(&satisfied_shapes)
900    }
901
902    /// Report which shapes the value can satisfy after `Ambiguous` result from `probe_key`.
903    ///
904    /// This is the shape-based version of `satisfy`, used when disambiguating
905    /// by nested field types. The deserializer should pass the shapes that
906    /// the actual value can be parsed into.
907    ///
908    /// # Example
909    ///
910    /// ```rust
911    /// use facet::Facet;
912    /// use facet_solver::{Schema, Solver, KeyResult, SatisfyResult};
913    ///
914    /// #[derive(Facet)]
915    /// struct SmallPayload { value: u8 }
916    ///
917    /// #[derive(Facet)]
918    /// struct LargePayload { value: u16 }
919    ///
920    /// #[derive(Facet)]
921    /// #[repr(u8)]
922    /// enum PayloadKind {
923    ///     Small { payload: SmallPayload },
924    ///     Large { payload: LargePayload },
925    /// }
926    ///
927    /// #[derive(Facet)]
928    /// struct Container {
929    ///     #[facet(flatten)]
930    ///     inner: PayloadKind,
931    /// }
932    ///
933    /// let schema = Schema::build(Container::SHAPE).unwrap();
934    /// let mut solver = Solver::new(&schema);
935    ///
936    /// // Report nested key
937    /// solver.probe_key(&[], "payload");
938    ///
939    /// // At payload.value, value is 1000 - doesn't fit u8
940    /// // Get shapes at this path
941    /// let shapes = solver.get_shapes_at_path(&["payload", "value"]);
942    /// // Filter to shapes that can hold 1000
943    /// let works: Vec<_> = shapes.iter()
944    ///     .filter(|s| s.type_identifier == "u16")
945    ///     .copied()
946    ///     .collect();
947    /// solver.satisfy_shapes(&works);
948    /// ```
949    pub fn satisfy_shapes(&mut self, satisfied_shapes: &[&'static Shape]) -> SatisfyResult<'a> {
950        if satisfied_shapes.is_empty() {
951            self.candidates = ConfigSet::empty(self.schema.configurations.len());
952            return SatisfyResult::NoMatch;
953        }
954
955        let mut new_candidates = ConfigSet::empty(self.schema.configurations.len());
956        for idx in self.candidates.iter() {
957            let config = &self.schema.configurations[idx];
958            // Check if any of this config's fields match the satisfied shapes
959            for field in config.fields.values() {
960                if satisfied_shapes
961                    .iter()
962                    .any(|s| core::ptr::eq(*s, field.value_shape))
963                {
964                    new_candidates.insert(idx);
965                    break;
966                }
967            }
968        }
969        self.candidates = new_candidates;
970
971        match self.candidates.len() {
972            0 => SatisfyResult::NoMatch,
973            1 => {
974                let idx = self.candidates.first().unwrap();
975                SatisfyResult::Solved(&self.schema.configurations[idx])
976            }
977            _ => SatisfyResult::Continue,
978        }
979    }
980
981    /// Get the shapes at a nested path across all remaining candidates.
982    ///
983    /// This is useful when you have an `Ambiguous` result from `probe_key`
984    /// and need to know what types are possible at that path.
985    pub fn get_shapes_at_path(&self, path: &[&str]) -> Vec<&'static Shape> {
986        let mut shapes: Vec<&'static Shape> = Vec::new();
987        for idx in self.candidates.iter() {
988            let config = &self.schema.configurations[idx];
989            if let Some(shape) = self.get_shape_at_path(config, path) {
990                if !shapes.iter().any(|s| core::ptr::eq(*s, shape)) {
991                    shapes.push(shape);
992                }
993            }
994        }
995        shapes
996    }
997
998    /// Report which shapes at a nested path the value can satisfy.
999    ///
1000    /// This is the path-aware version of `satisfy_shapes`, used when disambiguating
1001    /// by nested field types after `probe_key`.
1002    ///
1003    /// - `path`: The full path to the field (e.g., `["payload", "value"]`)
1004    /// - `satisfied_shapes`: The shapes that the value can be parsed into
1005    pub fn satisfy_at_path(
1006        &mut self,
1007        path: &[&str],
1008        satisfied_shapes: &[&'static Shape],
1009    ) -> SatisfyResult<'a> {
1010        if satisfied_shapes.is_empty() {
1011            self.candidates = ConfigSet::empty(self.schema.configurations.len());
1012            return SatisfyResult::NoMatch;
1013        }
1014
1015        // Keep only candidates where the shape at this path is in the satisfied set
1016        let mut new_candidates = ConfigSet::empty(self.schema.configurations.len());
1017        for idx in self.candidates.iter() {
1018            let config = &self.schema.configurations[idx];
1019            if let Some(shape) = self.get_shape_at_path(config, path) {
1020                if satisfied_shapes.iter().any(|s| core::ptr::eq(*s, shape)) {
1021                    new_candidates.insert(idx);
1022                }
1023            }
1024        }
1025        self.candidates = new_candidates;
1026
1027        match self.candidates.len() {
1028            0 => SatisfyResult::NoMatch,
1029            1 => {
1030                let idx = self.candidates.first().unwrap();
1031                SatisfyResult::Solved(&self.schema.configurations[idx])
1032            }
1033            _ => SatisfyResult::Continue,
1034        }
1035    }
1036
1037    /// Get the current candidate configurations.
1038    pub fn candidates(&self) -> Vec<&'a Configuration> {
1039        self.candidates
1040            .iter()
1041            .map(|idx| &self.schema.configurations[idx])
1042            .collect()
1043    }
1044
1045    /// Get the seen keys.
1046    pub fn seen_keys(&self) -> &BTreeSet<&'a str> {
1047        &self.seen_keys
1048    }
1049
1050    /// Hint that a specific enum variant should be selected.
1051    ///
1052    /// This filters the candidates to only those configurations where at least one
1053    /// variant selection has the given variant name. This is useful for explicit
1054    /// type disambiguation via annotations (e.g., KDL type annotations like `(Http)node`).
1055    ///
1056    /// Returns `true` if at least one candidate remains after filtering, `false` if
1057    /// no candidates match the variant name (in which case candidates are unchanged).
1058    ///
1059    /// # Example
1060    ///
1061    /// ```rust
1062    /// use facet::Facet;
1063    /// use facet_solver::{Schema, Solver};
1064    ///
1065    /// #[derive(Facet)]
1066    /// struct HttpSource { url: String }
1067    ///
1068    /// #[derive(Facet)]
1069    /// struct GitSource { url: String, branch: String }
1070    ///
1071    /// #[derive(Facet)]
1072    /// #[repr(u8)]
1073    /// enum SourceKind {
1074    ///     Http(HttpSource),
1075    ///     Git(GitSource),
1076    /// }
1077    ///
1078    /// #[derive(Facet)]
1079    /// struct Source {
1080    ///     #[facet(flatten)]
1081    ///     kind: SourceKind,
1082    /// }
1083    ///
1084    /// let schema = Schema::build(Source::SHAPE).unwrap();
1085    /// let mut solver = Solver::new(&schema);
1086    ///
1087    /// // Without hint, both variants are candidates
1088    /// assert_eq!(solver.candidates().len(), 2);
1089    ///
1090    /// // Hint at Http variant
1091    /// assert!(solver.hint_variant("Http"));
1092    /// assert_eq!(solver.candidates().len(), 1);
1093    /// ```
1094    pub fn hint_variant(&mut self, variant_name: &str) -> bool {
1095        // Build a set of configs that have this variant name
1096        let mut matching = ConfigSet::empty(self.schema.configurations.len());
1097
1098        for idx in self.candidates.iter() {
1099            let config = &self.schema.configurations[idx];
1100            // Check if any variant selection matches the given name
1101            if config
1102                .variant_selections()
1103                .iter()
1104                .any(|vs| vs.variant_name == variant_name)
1105            {
1106                matching.insert(idx);
1107            }
1108        }
1109
1110        if matching.is_empty() {
1111            // No matches - keep candidates unchanged
1112            false
1113        } else {
1114            self.candidates = matching;
1115            true
1116        }
1117    }
1118
1119    /// Mark a key as seen without filtering candidates.
1120    ///
1121    /// This is useful when the key is known to be present through means other than
1122    /// parsing (e.g., type annotations). Call this after `hint_variant` to mark
1123    /// the variant name as seen so that `finish()` doesn't report it as missing.
1124    pub fn mark_seen(&mut self, key: &'a str) {
1125        self.seen_keys.insert(key);
1126    }
1127
1128    /// Report a key at a nested path. Returns what to do next.
1129    ///
1130    /// This is the depth-aware version of `see_key`. Use this when probing
1131    /// nested structures where disambiguation might require looking inside objects.
1132    ///
1133    /// - `path`: The ancestor keys (e.g., `["payload"]` when inside a payload object)
1134    /// - `key`: The key found at this level (e.g., `"value"`)
1135    ///
1136    /// # Example
1137    ///
1138    /// ```rust
1139    /// use facet::Facet;
1140    /// use facet_solver::{Schema, Solver, KeyResult};
1141    ///
1142    /// #[derive(Facet)]
1143    /// struct SmallPayload { value: u8 }
1144    ///
1145    /// #[derive(Facet)]
1146    /// struct LargePayload { value: u16 }
1147    ///
1148    /// #[derive(Facet)]
1149    /// #[repr(u8)]
1150    /// enum PayloadKind {
1151    ///     Small { payload: SmallPayload },
1152    ///     Large { payload: LargePayload },
1153    /// }
1154    ///
1155    /// #[derive(Facet)]
1156    /// struct Container {
1157    ///     #[facet(flatten)]
1158    ///     inner: PayloadKind,
1159    /// }
1160    ///
1161    /// let schema = Schema::build(Container::SHAPE).unwrap();
1162    /// let mut solver = Solver::new(&schema);
1163    ///
1164    /// // "payload" exists in both - keep going
1165    /// solver.probe_key(&[], "payload");
1166    ///
1167    /// // "value" inside payload - both have it but different types!
1168    /// match solver.probe_key(&["payload"], "value") {
1169    ///     KeyResult::Ambiguous { fields } => {
1170    ///         // fields is Vec<(&FieldInfo, u64)> - field + specificity score
1171    ///         // Deserializer checks: 1000 fits u16 but not u8
1172    ///         // When multiple match, pick the one with lowest score (most specific)
1173    ///     }
1174    ///     _ => {}
1175    /// }
1176    /// ```
1177    pub fn probe_key(&mut self, path: &[&str], key: &str) -> KeyResult<'a> {
1178        // Build full path
1179        let mut full_path: Vec<&str> = path.to_vec();
1180        full_path.push(key);
1181
1182        // Filter candidates to only those that have this key path
1183        let mut new_candidates = ConfigSet::empty(self.schema.configurations.len());
1184        for idx in self.candidates.iter() {
1185            let config = &self.schema.configurations[idx];
1186            if config.has_key_path(&full_path) {
1187                new_candidates.insert(idx);
1188            }
1189        }
1190        self.candidates = new_candidates;
1191
1192        if self.candidates.is_empty() {
1193            return KeyResult::Unknown;
1194        }
1195
1196        // Check if we've disambiguated to exactly one
1197        if self.candidates.len() == 1 {
1198            let idx = self.candidates.first().unwrap();
1199            return KeyResult::Solved(&self.schema.configurations[idx]);
1200        }
1201
1202        // Get the shape at this path for each remaining candidate
1203        // We need to traverse the type tree to find the actual field type
1204        let mut unique_shapes: Vec<(&'static Shape, usize)> = Vec::new(); // (shape, config_idx)
1205
1206        for idx in self.candidates.iter() {
1207            let config = &self.schema.configurations[idx];
1208            if let Some(shape) = self.get_shape_at_path(config, &full_path) {
1209                // Deduplicate by shape pointer
1210                if !unique_shapes.iter().any(|(s, _)| core::ptr::eq(*s, shape)) {
1211                    unique_shapes.push((shape, idx));
1212                }
1213            }
1214        }
1215
1216        match unique_shapes.len() {
1217            0 => KeyResult::Unknown,
1218            1 => {
1219                // All candidates have the same type at this path - unambiguous
1220                KeyResult::Unambiguous {
1221                    shape: unique_shapes[0].0,
1222                }
1223            }
1224            _ => {
1225                // Different types at this path - need disambiguation
1226                // Build FieldInfo with scores for each unique shape
1227                let fields: Vec<(&'a FieldInfo, u64)> = unique_shapes
1228                    .iter()
1229                    .filter_map(|(shape, idx)| {
1230                        let config = &self.schema.configurations[*idx];
1231                        // For nested paths, we need the parent field
1232                        // e.g., for ["payload", "value"], get the "payload" field
1233                        let field = if path.is_empty() {
1234                            config.field(key)
1235                        } else {
1236                            // Return the top-level field that contains this path
1237                            config.field(path[0])
1238                        }?;
1239                        Some((field, specificity_score(shape)))
1240                    })
1241                    .collect();
1242
1243                KeyResult::Ambiguous { fields }
1244            }
1245        }
1246    }
1247
1248    /// Get the shape at a nested path within a configuration.
1249    fn get_shape_at_path(
1250        &self,
1251        config: &'a Configuration,
1252        path: &[&str],
1253    ) -> Option<&'static Shape> {
1254        if path.is_empty() {
1255            return None;
1256        }
1257
1258        // Start with the top-level field
1259        let top_field = config.field(path[0])?;
1260        let mut current_shape = top_field.value_shape;
1261
1262        // Navigate through nested structs
1263        for &key in &path[1..] {
1264            current_shape = self.get_field_shape(current_shape, key)?;
1265        }
1266
1267        Some(current_shape)
1268    }
1269
1270    /// Get the shape of a field within a struct shape.
1271    fn get_field_shape(&self, shape: &'static Shape, field_name: &str) -> Option<&'static Shape> {
1272        use facet_core::{StructType, Type, UserType};
1273
1274        match shape.ty {
1275            Type::User(UserType::Struct(StructType { fields, .. })) => {
1276                for field in fields {
1277                    if field.name == field_name {
1278                        return Some(field.shape());
1279                    }
1280                }
1281                None
1282            }
1283            _ => None,
1284        }
1285    }
1286
1287    /// Finish solving. Call this after all keys have been processed.
1288    ///
1289    /// This method is necessary because key-based filtering alone cannot disambiguate
1290    /// when one variant's required fields are a subset of another's.
1291    ///
1292    /// # Why not just use `see_key()` results?
1293    ///
1294    /// `see_key()` returns `Solved` when a key *excludes* candidates down to one.
1295    /// But when the input is a valid subset of multiple variants, no key excludes
1296    /// anything — you need `finish()` to check which candidates have all their
1297    /// required fields satisfied.
1298    ///
1299    /// # Example
1300    ///
1301    /// ```rust,ignore
1302    /// enum Source {
1303    ///     Http { url: String },                  // required: url
1304    ///     Git { url: String, branch: String },   // required: url, branch
1305    /// }
1306    /// ```
1307    ///
1308    /// | Input                  | `see_key` behavior                        | Resolution            |
1309    /// |------------------------|-------------------------------------------|-----------------------|
1310    /// | `{ "url", "branch" }`  | `branch` excludes `Http` → candidates = 1 | Early `Solved(Git)`   |
1311    /// | `{ "url" }`            | both have `url` → candidates = 2          | `finish()` → `Http`   |
1312    ///
1313    /// In the second case, no key ever excludes a candidate. Only `finish()` can
1314    /// determine that `Git` is missing its required `branch` field, leaving `Http`
1315    /// as the sole viable configuration.
1316    pub fn finish(self) -> Result<&'a Configuration, SolverError> {
1317        if self.candidates.is_empty() {
1318            return Err(SolverError::NoMatch {
1319                input_fields: self.seen_keys.iter().map(|s| (*s).into()).collect(),
1320                missing_required: Vec::new(),
1321                missing_required_detailed: Vec::new(),
1322                unknown_fields: Vec::new(),
1323                closest_config: None,
1324            });
1325        }
1326
1327        // Filter candidates to only those that have all required fields satisfied
1328        let viable: Vec<usize> = self
1329            .candidates
1330            .iter()
1331            .filter(|idx| {
1332                let config = &self.schema.configurations[*idx];
1333                config
1334                    .required_field_names
1335                    .iter()
1336                    .all(|f| self.seen_keys.contains(f))
1337            })
1338            .collect();
1339
1340        match viable.len() {
1341            0 => {
1342                let first_idx = self.candidates.first().unwrap();
1343                let first_config = &self.schema.configurations[first_idx];
1344                let missing: Vec<_> = first_config
1345                    .required_field_names
1346                    .iter()
1347                    .filter(|f| !self.seen_keys.contains(*f))
1348                    .copied()
1349                    .collect();
1350                let missing_detailed: Vec<_> = missing
1351                    .iter()
1352                    .filter_map(|name| first_config.field(name))
1353                    .map(MissingFieldInfo::from_field_info)
1354                    .collect();
1355                Err(SolverError::NoMatch {
1356                    input_fields: self.seen_keys.iter().map(|s| (*s).into()).collect(),
1357                    missing_required: missing,
1358                    missing_required_detailed: missing_detailed,
1359                    unknown_fields: Vec::new(),
1360                    closest_config: Some(first_config.describe()),
1361                })
1362            }
1363            _ => {
1364                // Return first viable (preserves variant declaration order)
1365                Ok(&self.schema.configurations[viable[0]])
1366            }
1367        }
1368    }
1369}
1370
1371// ============================================================================
1372// Probing Solver (Depth-Aware)
1373// ============================================================================
1374
1375/// Result of reporting a key to the probing solver.
1376#[derive(Debug)]
1377pub enum ProbeResult<'a> {
1378    /// Keep reporting keys - not yet disambiguated
1379    KeepGoing,
1380    /// Solved! Use this configuration
1381    Solved(&'a Configuration),
1382    /// No configuration matches the observed keys
1383    NoMatch,
1384}
1385
1386/// Depth-aware probing solver for streaming deserialization.
1387///
1388/// Unlike the batch solver or incremental solver, this solver accepts
1389/// key reports at arbitrary depths. It's designed for the "peek" strategy:
1390///
1391/// 1. Deserializer scans keys (without parsing values) and reports them
1392/// 2. Solver filters candidates based on which configs have that key path
1393/// 3. Once one candidate remains, solver returns `Solved`
1394/// 4. Deserializer rewinds and parses into the resolved type
1395///
1396/// # Example
1397///
1398/// ```rust
1399/// use facet::Facet;
1400/// use facet_solver::{Schema, ProbingSolver, ProbeResult};
1401///
1402/// #[derive(Facet)]
1403/// struct TextPayload { content: String }
1404///
1405/// #[derive(Facet)]
1406/// struct BinaryPayload { bytes: Vec<u8> }
1407///
1408/// #[derive(Facet)]
1409/// #[repr(u8)]
1410/// enum MessageKind {
1411///     Text { payload: TextPayload },
1412///     Binary { payload: BinaryPayload },
1413/// }
1414///
1415/// #[derive(Facet)]
1416/// struct Message {
1417///     id: String,
1418///     #[facet(flatten)]
1419///     kind: MessageKind,
1420/// }
1421///
1422/// let schema = Schema::build(Message::SHAPE).unwrap();
1423/// let mut solver = ProbingSolver::new(&schema);
1424///
1425/// // "id" exists in both configs - keep going
1426/// assert!(matches!(solver.probe_key(&[], "id"), ProbeResult::KeepGoing));
1427///
1428/// // "payload" exists in both configs - keep going
1429/// assert!(matches!(solver.probe_key(&[], "payload"), ProbeResult::KeepGoing));
1430///
1431/// // "content" inside payload only exists in Text - solved!
1432/// match solver.probe_key(&["payload"], "content") {
1433///     ProbeResult::Solved(config) => {
1434///         assert!(config.has_key_path(&["payload", "content"]));
1435///     }
1436///     _ => panic!("expected Solved"),
1437/// }
1438/// ```
1439#[derive(Debug)]
1440pub struct ProbingSolver<'a> {
1441    /// Remaining candidate configurations
1442    candidates: Vec<&'a Configuration>,
1443}
1444
1445impl<'a> ProbingSolver<'a> {
1446    /// Create a new probing solver from a schema.
1447    pub fn new(schema: &'a Schema) -> Self {
1448        Self {
1449            candidates: schema.configurations.iter().collect(),
1450        }
1451    }
1452
1453    /// Create a new probing solver from configurations directly.
1454    pub fn from_configurations(configs: &'a [Configuration]) -> Self {
1455        Self {
1456            candidates: configs.iter().collect(),
1457        }
1458    }
1459
1460    /// Report a key found at a path during probing.
1461    ///
1462    /// - `path`: The ancestor keys (e.g., `["payload"]` when inside the payload object)
1463    /// - `key`: The key found at this level (e.g., `"content"`)
1464    ///
1465    /// Returns what to do next.
1466    pub fn probe_key(&mut self, path: &[&str], key: &str) -> ProbeResult<'a> {
1467        // Build the full key path (runtime strings, compared against static schema)
1468        let mut full_path: Vec<&str> = path.to_vec();
1469        full_path.push(key);
1470
1471        // Filter to candidates that have this key path
1472        self.candidates.retain(|c| c.has_key_path(&full_path));
1473
1474        match self.candidates.len() {
1475            0 => ProbeResult::NoMatch,
1476            1 => ProbeResult::Solved(self.candidates[0]),
1477            _ => ProbeResult::KeepGoing,
1478        }
1479    }
1480
1481    /// Get the current candidate configurations.
1482    pub fn candidates(&self) -> &[&'a Configuration] {
1483        &self.candidates
1484    }
1485
1486    /// Finish probing - returns Solved if exactly one candidate remains.
1487    pub fn finish(&self) -> ProbeResult<'a> {
1488        match self.candidates.len() {
1489            0 => ProbeResult::NoMatch,
1490            1 => ProbeResult::Solved(self.candidates[0]),
1491            _ => ProbeResult::KeepGoing, // Still ambiguous
1492        }
1493    }
1494}
1495
1496// ============================================================================
1497// Schema Builder
1498// ============================================================================
1499
1500/// How enum variants are represented in the serialized format.
1501#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
1502pub enum EnumRepr {
1503    /// Variant fields are flattened to the same level as other fields.
1504    /// Used by formats like KDL, TOML where all fields appear at one level.
1505    /// Example: `{"name": "...", "host": "...", "port": 8080}`
1506    #[default]
1507    Flattened,
1508
1509    /// Variant name is a key, variant content is nested under it.
1510    /// Used by JSON with externally-tagged enums.
1511    /// Example: `{"name": "...", "Tcp": {"host": "...", "port": 8080}}`
1512    ExternallyTagged,
1513}
1514
1515impl Schema {
1516    /// Build a schema for the given shape with flattened enum representation.
1517    ///
1518    /// Returns an error if the type definition contains conflicts, such as
1519    /// duplicate field names from parent and flattened structs.
1520    pub fn build(shape: &'static Shape) -> Result<Self, SchemaError> {
1521        Self::build_with_repr(shape, EnumRepr::Flattened)
1522    }
1523
1524    /// Build a schema for externally-tagged enum representation (e.g., JSON).
1525    ///
1526    /// In this representation, the variant name appears as a key and the
1527    /// variant's content is nested under it. The solver will only expect
1528    /// to see the variant name as a top-level key, not the variant's fields.
1529    pub fn build_externally_tagged(shape: &'static Shape) -> Result<Self, SchemaError> {
1530        Self::build_with_repr(shape, EnumRepr::ExternallyTagged)
1531    }
1532
1533    /// Build a schema with the specified enum representation.
1534    pub fn build_with_repr(shape: &'static Shape, repr: EnumRepr) -> Result<Self, SchemaError> {
1535        let builder = SchemaBuilder::new(shape, repr);
1536        builder.into_schema()
1537    }
1538
1539    /// Get the configurations for this schema.
1540    pub fn configurations(&self) -> &[Configuration] {
1541        &self.configurations
1542    }
1543}
1544
1545impl Configuration {
1546    /// Create a new empty configuration.
1547    fn new() -> Self {
1548        Self {
1549            variant_selections: Vec::new(),
1550            fields: BTreeMap::new(),
1551            required_field_names: BTreeSet::new(),
1552            known_paths: BTreeSet::new(),
1553        }
1554    }
1555
1556    /// Add a key path (for depth-aware probing).
1557    fn add_key_path(&mut self, path: KeyPath) {
1558        self.known_paths.insert(path);
1559    }
1560
1561    /// Add a field to this configuration.
1562    ///
1563    /// Returns an error if a field with the same serialized name already exists
1564    /// but comes from a different source (different path). This catches duplicate
1565    /// field name conflicts between parent structs and flattened fields.
1566    fn add_field(&mut self, info: FieldInfo) -> Result<(), SchemaError> {
1567        if let Some(existing) = self.fields.get(info.serialized_name) {
1568            if existing.path != info.path {
1569                return Err(SchemaError::DuplicateField {
1570                    field_name: info.serialized_name,
1571                    first_path: existing.path.clone(),
1572                    second_path: info.path,
1573                });
1574            }
1575        }
1576        if info.required {
1577            self.required_field_names.insert(info.serialized_name);
1578        }
1579        self.fields.insert(info.serialized_name, info);
1580        Ok(())
1581    }
1582
1583    /// Add a variant selection to this configuration.
1584    fn add_variant_selection(
1585        &mut self,
1586        path: FieldPath,
1587        enum_name: &'static str,
1588        variant_name: &'static str,
1589    ) {
1590        self.variant_selections.push(VariantSelection {
1591            path,
1592            enum_name,
1593            variant_name,
1594        });
1595    }
1596
1597    /// Merge another configuration into this one.
1598    ///
1599    /// Returns an error if a field with the same serialized name already exists
1600    /// but comes from a different source (different path). This catches duplicate
1601    /// field name conflicts between parent structs and flattened fields.
1602    fn merge(&mut self, other: &Configuration) -> Result<(), SchemaError> {
1603        for (name, info) in &other.fields {
1604            if let Some(existing) = self.fields.get(*name) {
1605                if existing.path != info.path {
1606                    return Err(SchemaError::DuplicateField {
1607                        field_name: name,
1608                        first_path: existing.path.clone(),
1609                        second_path: info.path.clone(),
1610                    });
1611                }
1612            }
1613            self.fields.insert(*name, info.clone());
1614            if info.required {
1615                self.required_field_names.insert(*name);
1616            }
1617        }
1618        for vs in &other.variant_selections {
1619            self.variant_selections.push(vs.clone());
1620        }
1621        for path in &other.known_paths {
1622            self.known_paths.insert(path.clone());
1623        }
1624        Ok(())
1625    }
1626
1627    /// Mark all fields as optional (required = false).
1628    /// Used when a flattened field is wrapped in `Option<T>`.
1629    fn mark_all_optional(&mut self) {
1630        self.required_field_names.clear();
1631        for info in self.fields.values_mut() {
1632            info.required = false;
1633        }
1634    }
1635
1636    /// Check if this configuration matches the input fields.
1637    pub fn matches(&self, input_fields: &BTreeSet<&str>) -> MatchResult {
1638        let mut missing_required = Vec::new();
1639        let mut missing_optional = Vec::new();
1640
1641        for (name, info) in &self.fields {
1642            if !input_fields.contains(name) {
1643                if info.required {
1644                    missing_required.push(*name);
1645                } else {
1646                    missing_optional.push(*name);
1647                }
1648            }
1649        }
1650
1651        // Check for unknown fields
1652        let unknown: Vec<String> = input_fields
1653            .iter()
1654            .filter(|f| !self.fields.contains_key(*f))
1655            .map(|s| (*s).into())
1656            .collect();
1657
1658        if !missing_required.is_empty() || !unknown.is_empty() {
1659            MatchResult::NoMatch {
1660                missing_required,
1661                unknown,
1662            }
1663        } else if missing_optional.is_empty() {
1664            MatchResult::Exact
1665        } else {
1666            MatchResult::WithOptionalMissing(missing_optional)
1667        }
1668    }
1669
1670    /// Get a human-readable description of this configuration.
1671    ///
1672    /// Returns something like `MessagePayload::Text` or `Auth::Token + Transport::Tcp`
1673    /// for configurations with multiple variant selections.
1674    pub fn describe(&self) -> String {
1675        if self.variant_selections.is_empty() {
1676            String::from("(no variants)")
1677        } else {
1678            let parts: Vec<_> = self
1679                .variant_selections
1680                .iter()
1681                .map(|vs| alloc::format!("{}::{}", vs.enum_name, vs.variant_name))
1682                .collect();
1683            parts.join(" + ")
1684        }
1685    }
1686
1687    /// Get the fields in deserialization order (deepest first).
1688    pub fn deserialization_order(&self) -> Vec<&FieldInfo> {
1689        let mut fields: Vec<_> = self.fields.values().collect();
1690        fields.sort_by(|a, b| {
1691            // Deeper paths first
1692            b.path
1693                .depth()
1694                .cmp(&a.path.depth())
1695                // Then lexicographic for determinism
1696                .then_with(|| a.path.cmp(&b.path))
1697        });
1698        fields
1699    }
1700
1701    /// Get a field by name.
1702    pub fn field(&self, name: &str) -> Option<&FieldInfo> {
1703        self.fields.get(name)
1704    }
1705
1706    /// Get all fields.
1707    pub fn fields(&self) -> &BTreeMap<&'static str, FieldInfo> {
1708        &self.fields
1709    }
1710
1711    /// Get optional fields that were NOT provided in the input.
1712    ///
1713    /// This is useful for deserializers that need to initialize missing
1714    /// optional fields to `None` or their default value.
1715    ///
1716    /// # Example
1717    ///
1718    /// ```rust
1719    /// # use facet::Facet;
1720    /// # use facet_solver::Schema;
1721    /// # use std::collections::BTreeSet;
1722    /// #[derive(Facet)]
1723    /// struct Config {
1724    ///     required_field: String,
1725    ///     optional_field: Option<u32>,
1726    /// }
1727    ///
1728    /// let schema = Schema::build(Config::SHAPE).unwrap();
1729    /// let config = &schema.configurations()[0];
1730    ///
1731    /// // If we only saw "required_field"
1732    /// let mut seen = BTreeSet::new();
1733    /// seen.insert("required_field");
1734    ///
1735    /// let missing: Vec<_> = config.missing_optional_fields(&seen).collect();
1736    /// assert_eq!(missing.len(), 1);
1737    /// assert_eq!(missing[0].serialized_name, "optional_field");
1738    /// ```
1739    pub fn missing_optional_fields<'a>(
1740        &'a self,
1741        seen_keys: &'a BTreeSet<&str>,
1742    ) -> impl Iterator<Item = &'a FieldInfo> {
1743        self.fields
1744            .values()
1745            .filter(move |info| !info.required && !seen_keys.contains(info.serialized_name))
1746    }
1747
1748    /// Get variant selections.
1749    pub fn variant_selections(&self) -> &[VariantSelection] {
1750        &self.variant_selections
1751    }
1752
1753    /// Get all known key paths (for depth-aware probing).
1754    pub fn known_paths(&self) -> &BTreeSet<KeyPath> {
1755        &self.known_paths
1756    }
1757
1758    /// Check if this configuration has a specific key path.
1759    /// Compares runtime strings against static schema paths.
1760    pub fn has_key_path(&self, path: &[&str]) -> bool {
1761        self.known_paths.iter().any(|known| {
1762            known.len() == path.len() && known.iter().zip(path.iter()).all(|(a, b)| *a == *b)
1763        })
1764    }
1765}
1766
1767struct SchemaBuilder {
1768    shape: &'static Shape,
1769    enum_repr: EnumRepr,
1770}
1771
1772impl SchemaBuilder {
1773    fn new(shape: &'static Shape, enum_repr: EnumRepr) -> Self {
1774        Self { shape, enum_repr }
1775    }
1776
1777    fn analyze(&self) -> Result<Vec<Configuration>, SchemaError> {
1778        self.analyze_shape(self.shape, FieldPath::empty(), Vec::new())
1779    }
1780
1781    /// Analyze a shape and return all possible configurations.
1782    /// Returns a Vec because enums create multiple configurations.
1783    ///
1784    /// - `current_path`: The internal field path (for FieldInfo)
1785    /// - `key_prefix`: The serialized key path prefix (for known_paths)
1786    fn analyze_shape(
1787        &self,
1788        shape: &'static Shape,
1789        current_path: FieldPath,
1790        key_prefix: KeyPath,
1791    ) -> Result<Vec<Configuration>, SchemaError> {
1792        match shape.ty {
1793            Type::User(UserType::Struct(struct_type)) => {
1794                self.analyze_struct(struct_type, current_path, key_prefix)
1795            }
1796            Type::User(UserType::Enum(enum_type)) => {
1797                // Enum at root level: create one configuration per variant
1798                self.analyze_enum(shape, enum_type, current_path, key_prefix)
1799            }
1800            _ => {
1801                // For non-struct types at root level, return single empty config
1802                Ok(vec![Configuration::new()])
1803            }
1804        }
1805    }
1806
1807    /// Analyze an enum and return one configuration per variant.
1808    ///
1809    /// - `current_path`: The internal field path (for FieldInfo)
1810    /// - `key_prefix`: The serialized key path prefix (for known_paths)
1811    fn analyze_enum(
1812        &self,
1813        shape: &'static Shape,
1814        enum_type: facet_core::EnumType,
1815        current_path: FieldPath,
1816        key_prefix: KeyPath,
1817    ) -> Result<Vec<Configuration>, SchemaError> {
1818        let enum_name = shape.type_identifier;
1819        let mut result = Vec::new();
1820
1821        for variant in enum_type.variants {
1822            let mut config = Configuration::new();
1823
1824            // Record this variant selection
1825            config.add_variant_selection(current_path.clone(), enum_name, variant.name);
1826
1827            let variant_path = current_path.push_variant("", variant.name);
1828
1829            // Get configurations from the variant's content
1830            let variant_configs =
1831                self.analyze_variant_content(variant, &variant_path, &key_prefix)?;
1832
1833            // Merge each variant config into the base
1834            for variant_config in variant_configs {
1835                let mut final_config = config.clone();
1836                final_config.merge(&variant_config)?;
1837                result.push(final_config);
1838            }
1839        }
1840
1841        Ok(result)
1842    }
1843
1844    /// Analyze a struct and return all possible configurations.
1845    ///
1846    /// - `current_path`: The internal field path (for FieldInfo)
1847    /// - `key_prefix`: The serialized key path prefix (for known_paths)
1848    fn analyze_struct(
1849        &self,
1850        struct_type: StructType,
1851        current_path: FieldPath,
1852        key_prefix: KeyPath,
1853    ) -> Result<Vec<Configuration>, SchemaError> {
1854        // Start with one empty configuration
1855        let mut configs = vec![Configuration::new()];
1856
1857        // Process each field, potentially multiplying configurations
1858        for field in struct_type.fields {
1859            configs =
1860                self.analyze_field_into_configs(field, &current_path, &key_prefix, configs)?;
1861        }
1862
1863        Ok(configs)
1864    }
1865
1866    /// Process a field and return updated configurations.
1867    /// If the field is a flattened enum, this may multiply the number of configs.
1868    ///
1869    /// - `parent_path`: The internal field path to the parent (for FieldInfo)
1870    /// - `key_prefix`: The serialized key path prefix (for known_paths)
1871    fn analyze_field_into_configs(
1872        &self,
1873        field: &'static Field,
1874        parent_path: &FieldPath,
1875        key_prefix: &KeyPath,
1876        mut configs: Vec<Configuration>,
1877    ) -> Result<Vec<Configuration>, SchemaError> {
1878        let is_flatten = field.flags.contains(FieldFlags::FLATTEN);
1879
1880        if is_flatten {
1881            // Flattened: inner keys bubble up to current level (same key_prefix)
1882            self.analyze_flattened_field_into_configs(field, parent_path, key_prefix, configs)
1883        } else {
1884            // Regular field: add to ALL current configs
1885            let field_path = parent_path.push_field(field.name);
1886            let required =
1887                !field.flags.contains(FieldFlags::DEFAULT) && !is_option_type(field.shape());
1888
1889            // Build the key path for this field
1890            let mut field_key_path = key_prefix.clone();
1891            field_key_path.push(field.name);
1892
1893            let field_info = FieldInfo {
1894                serialized_name: field.name,
1895                path: field_path,
1896                required,
1897                value_shape: field.shape(),
1898            };
1899
1900            for config in &mut configs {
1901                config.add_field(field_info.clone())?;
1902                // Add this field's key path
1903                config.add_key_path(field_key_path.clone());
1904            }
1905
1906            // If the field's value is a struct, recurse to collect nested key paths
1907            // (for probing, not for flattening - these are nested in serialized format)
1908            // This may fork configurations if the nested struct contains flattened enums!
1909            configs =
1910                self.collect_nested_key_paths_for_shape(field.shape(), &field_key_path, configs)?;
1911
1912            Ok(configs)
1913        }
1914    }
1915
1916    /// Collect nested key paths from a shape into configurations.
1917    /// This handles the case where a non-flattened field contains a struct with flattened enums.
1918    /// Returns updated configurations (may fork if flattened enums are encountered).
1919    fn collect_nested_key_paths_for_shape(
1920        &self,
1921        shape: &'static Shape,
1922        key_prefix: &KeyPath,
1923        configs: Vec<Configuration>,
1924    ) -> Result<Vec<Configuration>, SchemaError> {
1925        match shape.ty {
1926            Type::User(UserType::Struct(struct_type)) => {
1927                self.collect_nested_key_paths_for_struct(struct_type, key_prefix, configs)
1928            }
1929            _ => Ok(configs),
1930        }
1931    }
1932
1933    /// Collect nested key paths from a struct, potentially forking for flattened enums.
1934    fn collect_nested_key_paths_for_struct(
1935        &self,
1936        struct_type: StructType,
1937        key_prefix: &KeyPath,
1938        mut configs: Vec<Configuration>,
1939    ) -> Result<Vec<Configuration>, SchemaError> {
1940        for field in struct_type.fields {
1941            let is_flatten = field.flags.contains(FieldFlags::FLATTEN);
1942            let mut field_key_path = key_prefix.clone();
1943
1944            if is_flatten {
1945                // Flattened field: keys bubble up to current level, may fork configs
1946                configs =
1947                    self.collect_nested_key_paths_for_flattened(field, key_prefix, configs)?;
1948            } else {
1949                // Regular field: add key path and recurse
1950                field_key_path.push(field.name);
1951
1952                for config in &mut configs {
1953                    config.add_key_path(field_key_path.clone());
1954                }
1955
1956                // Recurse into nested structs
1957                configs = self.collect_nested_key_paths_for_shape(
1958                    field.shape(),
1959                    &field_key_path,
1960                    configs,
1961                )?;
1962            }
1963        }
1964        Ok(configs)
1965    }
1966
1967    /// Handle flattened fields when collecting nested key paths.
1968    /// This may fork configurations for flattened enums.
1969    fn collect_nested_key_paths_for_flattened(
1970        &self,
1971        field: &'static Field,
1972        key_prefix: &KeyPath,
1973        configs: Vec<Configuration>,
1974    ) -> Result<Vec<Configuration>, SchemaError> {
1975        let shape = field.shape();
1976
1977        match shape.ty {
1978            Type::User(UserType::Struct(struct_type)) => {
1979                // Flattened struct: recurse with same key_prefix
1980                self.collect_nested_key_paths_for_struct(struct_type, key_prefix, configs)
1981            }
1982            Type::User(UserType::Enum(enum_type)) => {
1983                // Flattened enum: fork configurations
1984                // We need to match each config to its corresponding variant
1985                let mut result = Vec::new();
1986
1987                for config in configs {
1988                    // Find which variant this config has selected for this field
1989                    let selected_variant = config
1990                        .variant_selections
1991                        .iter()
1992                        .find(|vs| {
1993                            // Match by the field name in the path
1994                            vs.path.segments.last() == Some(&PathSegment::Field(field.name))
1995                        })
1996                        .map(|vs| vs.variant_name);
1997
1998                    if let Some(variant_name) = selected_variant {
1999                        // Find the variant and collect its key paths
2000                        if let Some(variant) =
2001                            enum_type.variants.iter().find(|v| v.name == variant_name)
2002                        {
2003                            let mut updated_config = config;
2004                            updated_config = self.collect_variant_key_paths(
2005                                variant,
2006                                key_prefix,
2007                                updated_config,
2008                            )?;
2009                            result.push(updated_config);
2010                        } else {
2011                            result.push(config);
2012                        }
2013                    } else {
2014                        result.push(config);
2015                    }
2016                }
2017                Ok(result)
2018            }
2019            _ => Ok(configs),
2020        }
2021    }
2022
2023    /// Collect key paths from an enum variant's content.
2024    fn collect_variant_key_paths(
2025        &self,
2026        variant: &'static Variant,
2027        key_prefix: &KeyPath,
2028        mut config: Configuration,
2029    ) -> Result<Configuration, SchemaError> {
2030        // Check if this is a newtype variant (single unnamed field)
2031        if variant.data.fields.len() == 1 && variant.data.fields[0].name == "0" {
2032            let inner_field = &variant.data.fields[0];
2033            let inner_shape = inner_field.shape();
2034
2035            // If the inner type is a struct, flatten its fields
2036            if let Type::User(UserType::Struct(inner_struct)) = inner_shape.ty {
2037                let configs = self.collect_nested_key_paths_for_struct(
2038                    inner_struct,
2039                    key_prefix,
2040                    vec![config],
2041                )?;
2042                return Ok(configs
2043                    .into_iter()
2044                    .next()
2045                    .unwrap_or_else(Configuration::new));
2046            }
2047        }
2048
2049        // Named fields - process each
2050        for variant_field in variant.data.fields {
2051            let is_flatten = variant_field.flags.contains(FieldFlags::FLATTEN);
2052
2053            if is_flatten {
2054                let configs = self.collect_nested_key_paths_for_flattened(
2055                    variant_field,
2056                    key_prefix,
2057                    vec![config],
2058                )?;
2059                config = configs
2060                    .into_iter()
2061                    .next()
2062                    .unwrap_or_else(Configuration::new);
2063            } else {
2064                let mut field_key_path = key_prefix.clone();
2065                field_key_path.push(variant_field.name);
2066                config.add_key_path(field_key_path.clone());
2067
2068                let configs = self.collect_nested_key_paths_for_shape(
2069                    variant_field.shape(),
2070                    &field_key_path,
2071                    vec![config],
2072                )?;
2073                config = configs
2074                    .into_iter()
2075                    .next()
2076                    .unwrap_or_else(Configuration::new);
2077            }
2078        }
2079        Ok(config)
2080    }
2081
2082    /// Collect ONLY key paths from a variant's content (no fields added).
2083    /// Used for externally-tagged enums where variant content is nested and
2084    /// will be parsed separately by the deserializer.
2085    fn collect_variant_key_paths_only(
2086        &self,
2087        variant: &'static Variant,
2088        key_prefix: &KeyPath,
2089        config: &mut Configuration,
2090    ) -> Result<(), SchemaError> {
2091        // Check if this is a newtype variant (single unnamed field)
2092        if variant.data.fields.len() == 1 && variant.data.fields[0].name == "0" {
2093            let inner_field = &variant.data.fields[0];
2094            let inner_shape = inner_field.shape();
2095
2096            // If the inner type is a struct, add key paths for its fields
2097            if let Type::User(UserType::Struct(inner_struct)) = inner_shape.ty {
2098                Self::collect_struct_key_paths_only(inner_struct, key_prefix, config);
2099                return Ok(());
2100            }
2101        }
2102
2103        // Named fields - add key paths for each
2104        for variant_field in variant.data.fields {
2105            let mut field_key_path = key_prefix.clone();
2106            field_key_path.push(variant_field.name);
2107            config.add_key_path(field_key_path.clone());
2108
2109            // Recurse into nested structs
2110            if let Type::User(UserType::Struct(inner_struct)) = variant_field.shape().ty {
2111                Self::collect_struct_key_paths_only(inner_struct, &field_key_path, config);
2112            }
2113        }
2114        Ok(())
2115    }
2116
2117    /// Recursively collect key paths from a struct (no fields added).
2118    fn collect_struct_key_paths_only(
2119        struct_type: StructType,
2120        key_prefix: &KeyPath,
2121        config: &mut Configuration,
2122    ) {
2123        for field in struct_type.fields {
2124            let is_flatten = field.flags.contains(FieldFlags::FLATTEN);
2125
2126            if is_flatten {
2127                // Flattened field: keys bubble up to current level
2128                if let Type::User(UserType::Struct(inner_struct)) = field.shape().ty {
2129                    Self::collect_struct_key_paths_only(inner_struct, key_prefix, config);
2130                }
2131            } else {
2132                // Regular field: add its key path
2133                let mut field_key_path = key_prefix.clone();
2134                field_key_path.push(field.name);
2135                config.add_key_path(field_key_path.clone());
2136
2137                // Recurse into nested structs
2138                if let Type::User(UserType::Struct(inner_struct)) = field.shape().ty {
2139                    Self::collect_struct_key_paths_only(inner_struct, &field_key_path, config);
2140                }
2141            }
2142        }
2143    }
2144
2145    /// Process a flattened field, potentially forking configurations for enums.
2146    ///
2147    /// For flattened fields, the inner keys bubble up to the current level,
2148    /// so we pass the same key_prefix (not key_prefix + field.name).
2149    ///
2150    /// If the field is `Option<T>`, we unwrap to get T and mark all resulting
2151    /// fields as optional (since the entire flattened block can be omitted).
2152    fn analyze_flattened_field_into_configs(
2153        &self,
2154        field: &'static Field,
2155        parent_path: &FieldPath,
2156        key_prefix: &KeyPath,
2157        configs: Vec<Configuration>,
2158    ) -> Result<Vec<Configuration>, SchemaError> {
2159        let field_path = parent_path.push_field(field.name);
2160        let original_shape = field.shape();
2161
2162        // Check if this is Option<T> - if so, unwrap and mark all fields optional
2163        let (shape, is_optional_flatten) = match unwrap_option_type(original_shape) {
2164            Some(inner) => (inner, true),
2165            None => (original_shape, false),
2166        };
2167
2168        match shape.ty {
2169            Type::User(UserType::Struct(struct_type)) => {
2170                // Flatten a struct: get its configurations and merge into each of ours
2171                // Key prefix stays the same - inner keys bubble up
2172                let mut struct_configs =
2173                    self.analyze_struct(struct_type, field_path, key_prefix.clone())?;
2174
2175                // If the flatten field was Option<T>, mark all inner fields as optional
2176                if is_optional_flatten {
2177                    for config in &mut struct_configs {
2178                        config.mark_all_optional();
2179                    }
2180                }
2181
2182                // Each of our configs combines with each struct config
2183                // (usually struct_configs has 1 element unless it contains enums)
2184                let mut result = Vec::new();
2185                for base_config in configs {
2186                    for struct_config in &struct_configs {
2187                        let mut merged = base_config.clone();
2188                        merged.merge(struct_config)?;
2189                        result.push(merged);
2190                    }
2191                }
2192                Ok(result)
2193            }
2194            Type::User(UserType::Enum(enum_type)) => {
2195                // Fork: each existing config × each variant
2196                let mut result = Vec::new();
2197                let enum_name = shape.type_identifier;
2198
2199                for base_config in configs {
2200                    for variant in enum_type.variants {
2201                        let mut forked = base_config.clone();
2202                        forked.add_variant_selection(field_path.clone(), enum_name, variant.name);
2203
2204                        let variant_path = field_path.push_variant(field.name, variant.name);
2205
2206                        match self.enum_repr {
2207                            EnumRepr::ExternallyTagged => {
2208                                // For externally tagged enums, the variant name is a key
2209                                // at the current level, and its content is nested underneath.
2210                                let mut variant_key_prefix = key_prefix.clone();
2211                                variant_key_prefix.push(variant.name);
2212
2213                                // Add the variant name itself as a known key path
2214                                forked.add_key_path(variant_key_prefix.clone());
2215
2216                                // Add the variant name as a field (the key that selects this variant)
2217                                let variant_field_info = FieldInfo {
2218                                    serialized_name: variant.name,
2219                                    path: variant_path.clone(),
2220                                    required: !is_optional_flatten,
2221                                    value_shape: shape, // The enum shape
2222                                };
2223                                forked.add_field(variant_field_info)?;
2224
2225                                // For externally-tagged enums, we do NOT add the variant's
2226                                // inner fields to required fields. They're nested and will
2227                                // be parsed separately by the deserializer.
2228                                // Only add them to known_paths for depth-aware probing.
2229                                self.collect_variant_key_paths_only(
2230                                    variant,
2231                                    &variant_key_prefix,
2232                                    &mut forked,
2233                                )?;
2234
2235                                result.push(forked);
2236                            }
2237                            EnumRepr::Flattened => {
2238                                // For flattened enums, the variant's fields appear at the
2239                                // same level as other fields. The variant name is NOT a key;
2240                                // only the variant's inner fields are keys.
2241
2242                                // Get configurations from the variant's content
2243                                // Key prefix stays the same - inner keys bubble up
2244                                let mut variant_configs = self.analyze_variant_content(
2245                                    variant,
2246                                    &variant_path,
2247                                    key_prefix,
2248                                )?;
2249
2250                                // If the flatten field was Option<T>, mark all inner fields as optional
2251                                if is_optional_flatten {
2252                                    for config in &mut variant_configs {
2253                                        config.mark_all_optional();
2254                                    }
2255                                }
2256
2257                                // Merge each variant config into the forked base
2258                                for variant_config in variant_configs {
2259                                    let mut final_config = forked.clone();
2260                                    final_config.merge(&variant_config)?;
2261                                    result.push(final_config);
2262                                }
2263                            }
2264                        }
2265                    }
2266                }
2267                Ok(result)
2268            }
2269            _ => {
2270                // Can't flatten other types - treat as regular field
2271                // For Option<T> flatten, also consider optionality from the wrapper
2272                let required = !field.flags.contains(FieldFlags::DEFAULT)
2273                    && !is_option_type(shape)
2274                    && !is_optional_flatten;
2275
2276                // For non-flattenable types, add the field with its key path
2277                let mut field_key_path = key_prefix.clone();
2278                field_key_path.push(field.name);
2279
2280                let field_info = FieldInfo {
2281                    serialized_name: field.name,
2282                    path: field_path,
2283                    required,
2284                    value_shape: shape,
2285                };
2286
2287                let mut result = configs;
2288                for config in &mut result {
2289                    config.add_field(field_info.clone())?;
2290                    config.add_key_path(field_key_path.clone());
2291                }
2292                Ok(result)
2293            }
2294        }
2295    }
2296
2297    /// Analyze a variant's content and return configurations.
2298    ///
2299    /// - `variant_path`: The internal field path (for FieldInfo)
2300    /// - `key_prefix`: The serialized key path prefix (for known_paths)
2301    fn analyze_variant_content(
2302        &self,
2303        variant: &'static Variant,
2304        variant_path: &FieldPath,
2305        key_prefix: &KeyPath,
2306    ) -> Result<Vec<Configuration>, SchemaError> {
2307        // Check if this is a newtype variant (single unnamed field like `Foo(Bar)`)
2308        if variant.data.fields.len() == 1 && variant.data.fields[0].name == "0" {
2309            let inner_field = &variant.data.fields[0];
2310            let inner_shape = inner_field.shape();
2311
2312            // If the inner type is a struct, flatten its fields
2313            // Key prefix stays the same - inner keys bubble up
2314            if let Type::User(UserType::Struct(inner_struct)) = inner_shape.ty {
2315                let inner_path = variant_path.push_field("0");
2316                return self.analyze_struct(inner_struct, inner_path, key_prefix.clone());
2317            }
2318        }
2319
2320        // Named fields or multiple fields - analyze as a pseudo-struct
2321        let mut configs = vec![Configuration::new()];
2322        for variant_field in variant.data.fields {
2323            configs =
2324                self.analyze_field_into_configs(variant_field, variant_path, key_prefix, configs)?;
2325        }
2326        Ok(configs)
2327    }
2328
2329    fn into_schema(self) -> Result<Schema, SchemaError> {
2330        let configurations = self.analyze()?;
2331        let num_configs = configurations.len();
2332
2333        // Build inverted index: field_name → bitmask of config indices
2334        let mut field_to_configs: BTreeMap<&'static str, ConfigSet> = BTreeMap::new();
2335        for (idx, config) in configurations.iter().enumerate() {
2336            for field_name in config.fields.keys() {
2337                field_to_configs
2338                    .entry(*field_name)
2339                    .or_insert_with(|| ConfigSet::empty(num_configs))
2340                    .insert(idx);
2341            }
2342        }
2343
2344        Ok(Schema {
2345            shape: self.shape,
2346            configurations,
2347            field_to_configs,
2348        })
2349    }
2350}
2351
2352/// Check if a shape represents an Option type.
2353fn is_option_type(shape: &'static Shape) -> bool {
2354    matches!(shape.def, Def::Option(_))
2355}
2356
2357/// If shape is `Option<T>`, returns `Some(T's shape)`. Otherwise returns `None`.
2358fn unwrap_option_type(shape: &'static Shape) -> Option<&'static Shape> {
2359    match shape.def {
2360        Def::Option(option_def) => Some(option_def.t),
2361        _ => None,
2362    }
2363}