Skip to main content

facet_format/
solver.rs

1extern crate alloc;
2
3use alloc::borrow::Cow;
4use alloc::sync::Arc;
5use core::fmt;
6use facet_core::Shape;
7
8use facet_solver::{KeyResult, Resolution, ResolutionHandle, Schema, Solver};
9
10use crate::{FormatParser, ParseError, ParseEventKind};
11
12/// High-level outcome from solving an untagged enum.
13pub struct SolveOutcome {
14    /// The schema that was used for solving
15    pub schema: Arc<Schema>,
16    /// Index of the chosen resolution in `schema.resolutions()`
17    pub resolution_index: usize,
18}
19
20/// Error when variant solving fails.
21#[derive(Debug)]
22#[non_exhaustive]
23pub enum SolveVariantError {
24    /// No variant matched the evidence.
25    NoMatch,
26    /// Parser error while reading events.
27    Parser(ParseError),
28    /// Schema construction error.
29    SchemaError(facet_solver::SchemaError),
30}
31
32impl SolveVariantError {
33    /// Wrap a parse error into [`SolveVariantError::Parser`].
34    pub const fn from_parser(e: ParseError) -> Self {
35        Self::Parser(e)
36    }
37}
38
39impl fmt::Display for SolveVariantError {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        match self {
42            Self::NoMatch => write!(f, "No variant matched"),
43            Self::Parser(e) => write!(f, "Parser error: {}", e),
44            Self::SchemaError(e) => write!(f, "Schema error: {}", e),
45        }
46    }
47}
48
49impl core::error::Error for SolveVariantError {}
50
51/// Attempt to solve which enum variant matches the input.
52///
53/// This uses save/restore to read ahead and determine the variant without
54/// consuming the events permanently. After this returns, the parser position
55/// is restored so the actual deserialization can proceed.
56///
57/// Returns `Ok(Some(_))` if a unique variant was found, `Ok(None)` if
58/// no variant matched, or `Err(_)` on error.
59pub fn solve_variant<'de>(
60    shape: &'static Shape,
61    parser: &mut dyn FormatParser<'de>,
62) -> Result<Option<SolveOutcome>, SolveVariantError> {
63    let schema = Arc::new(Schema::build_auto(shape)?);
64    let mut solver = Solver::new(&schema);
65
66    // Save position and start recording events
67    let save_point = parser.save();
68
69    let mut depth = 0i32;
70    let mut in_struct = false;
71    let mut expecting_value = false;
72
73    let result = loop {
74        let event = parser
75            .next_event()
76            .map_err(SolveVariantError::from_parser)?;
77
78        let Some(event) = event else {
79            // EOF reached
80            return Ok(None);
81        };
82
83        match event.kind {
84            ParseEventKind::StructStart(_) => {
85                depth += 1;
86                if depth == 1 {
87                    in_struct = true;
88                }
89            }
90            ParseEventKind::StructEnd => {
91                depth -= 1;
92                if depth == 0 {
93                    // Done with top-level struct
94                    break None;
95                }
96            }
97            ParseEventKind::SequenceStart(_) => {
98                depth += 1;
99            }
100            ParseEventKind::SequenceEnd => {
101                depth -= 1;
102            }
103            ParseEventKind::FieldKey(key) => {
104                if depth == 1 && in_struct {
105                    // Top-level field - feed to solver
106                    if let Some(name) = key.name().cloned()
107                        && let Some(handle) = handle_key(&mut solver, name)
108                    {
109                        break Some(handle);
110                    }
111                    expecting_value = true;
112                }
113            }
114            ParseEventKind::Scalar(_)
115            | ParseEventKind::OrderedField
116            | ParseEventKind::VariantTag(_) => {
117                if expecting_value {
118                    expecting_value = false;
119                }
120            }
121        }
122    };
123
124    // Restore position regardless of outcome
125    parser.restore(save_point);
126
127    match result {
128        Some(handle) => {
129            let idx = handle.index();
130            Ok(Some(SolveOutcome {
131                schema,
132                resolution_index: idx,
133            }))
134        }
135        None => Ok(None),
136    }
137}
138
139fn handle_key<'a>(solver: &mut Solver<'a>, name: Cow<'a, str>) -> Option<ResolutionHandle<'a>> {
140    match solver.see_key(name) {
141        KeyResult::Solved(handle) => Some(handle),
142        KeyResult::Unknown | KeyResult::Unambiguous { .. } | KeyResult::Ambiguous { .. } => None,
143        // A key result added since this match was written.
144        _ => None,
145    }
146}
147
148impl From<facet_solver::SchemaError> for SolveVariantError {
149    fn from(e: facet_solver::SchemaError) -> Self {
150        Self::SchemaError(e)
151    }
152}
153
154impl SolveOutcome {
155    /// Resolve the selected configuration reference.
156    pub fn resolution(&self) -> &Resolution {
157        &self.schema.resolutions()[self.resolution_index]
158    }
159}