Skip to main content

animsmith_engine/
error.rs

1use crate::{
2    ProfileSelection, SettingDomain, SettingId, SettingScope, SettingValue, SettingValueKind,
3};
4use animsmith_core::SourceFormatV1;
5use animsmith_core::engine_contract::EngineContractError;
6use std::fmt;
7
8/// Location of one settings declaration.
9#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
10pub enum SettingLocation {
11    /// Document-wide engine settings.
12    Document,
13    /// Settings declared under one clip selector.
14    ClipSelector(String),
15}
16
17impl fmt::Display for SettingLocation {
18    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
19        match self {
20            Self::Document => formatter.write_str("document settings"),
21            Self::ClipSelector(selector) => write!(formatter, "clip selector {selector:?}"),
22        }
23    }
24}
25
26/// Why a closed setting value failed its declared domain.
27#[derive(Debug, Clone, PartialEq, Eq)]
28pub enum InvalidSettingReason {
29    /// The value variant does not match the descriptor domain.
30    WrongDomain {
31        /// Required descriptor domain.
32        expected: SettingDomain,
33        /// Supplied closed value kind.
34        found: SettingValueKind,
35    },
36    /// The transform path is empty.
37    EmptyPath,
38    /// The transform path exceeds the V1 UTF-8 byte limit.
39    PathTooLong {
40        /// Actual UTF-8 byte count.
41        bytes: usize,
42        /// Maximum UTF-8 byte count.
43        limit: usize,
44    },
45    /// The transform path begins with `/` and is not relative.
46    AbsolutePath,
47    /// The transform path contains an empty segment.
48    EmptyPathSegment,
49    /// The transform path contains `.` or `..`.
50    DotPathSegment,
51    /// The transform path contains an ASCII control character.
52    ControlCharacter,
53}
54
55/// Typed failure from static or input-dependent engine resolution.
56#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
57#[non_exhaustive]
58pub enum ResolutionError {
59    /// Settings were declared without selecting an engine profile.
60    #[error("engine settings were declared without an engine profile selection")]
61    SettingsWithoutSelection,
62    /// No exact registry tuple matches the selection.
63    #[error("unknown engine profile selection {0:?}")]
64    UnknownProfile(ProfileSelection),
65    /// A setting key is not in the selected profile's descriptor inventory.
66    #[error("unknown engine setting {key:?} in {location}")]
67    UnknownSetting {
68        /// Supplied setting key.
69        key: String,
70        /// Declaration location.
71        location: SettingLocation,
72    },
73    /// A known setting was declared in the wrong scope.
74    #[error("engine setting {setting} has {expected:?} scope but was declared in {found:?} scope")]
75    WrongScope {
76        /// Stable setting id.
77        setting: SettingId,
78        /// Descriptor scope.
79        expected: SettingScope,
80        /// Supplied scope.
81        found: SettingScope,
82        /// Declaration location.
83        location: SettingLocation,
84    },
85    /// A known descriptor genuinely does not apply to the selected profile.
86    #[error("engine setting {setting} is not applicable in {location}")]
87    NotApplicable {
88        /// Stable setting id.
89        setting: SettingId,
90        /// Declaration location.
91        location: SettingLocation,
92    },
93    /// A setting value is outside its closed descriptor domain.
94    #[error("invalid value for engine setting {setting} in {location}: {reason:?}")]
95    InvalidSettingValue {
96        /// Stable setting id.
97        setting: SettingId,
98        /// Declaration location.
99        location: SettingLocation,
100        /// Exact domain/path failure.
101        reason: InvalidSettingReason,
102    },
103    /// One required-without-default setting was not materialized.
104    #[error("missing required engine setting {setting} in {location}")]
105    MissingRequiredSetting {
106        /// Stable setting id.
107        setting: SettingId,
108        /// Document or exact real clip requiring the setting.
109        location: SettingLocation,
110    },
111    /// The authoritative input container is outside the selected V1 boundary.
112    #[error("input format {format:?} is not accepted by engine profile {selection:?}")]
113    UnacceptedInputFormat {
114        /// Selected exact profile tuple.
115        selection: ProfileSelection,
116        /// Authoritative loader-owned input format.
117        format: SourceFormatV1,
118    },
119    /// Fully materialized settings exceeded or contradicted the bounded V1
120    /// engine contract.
121    #[error("resolved engine settings are outside the V1 contract: {0}")]
122    ResolvedSettingsContract(#[from] EngineContractError),
123}
124
125/// Typed failure while binding an engine-owned check to immutable evidence.
126#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
127#[non_exhaustive]
128pub enum PredictionRuleError {
129    /// The supplied provenance was not projected from the supplied same-load
130    /// source facts and dependency closure.
131    #[error("prediction provenance does not describe the supplied same-load source")]
132    SourceProvenanceMismatch,
133    /// An exact built-in profile tuple carried facts other than the immutable
134    /// registry record for that revision.
135    #[error("prediction provenance does not match the frozen engine profile facts")]
136    FrozenProfileMismatch,
137}
138
139/// Author-owned invariant failure in the built-in V1 registry.
140#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
141#[non_exhaustive]
142pub enum RegistryValidationError {
143    /// V1 did not contain exactly five singleton profiles.
144    #[error("V1 registry contains {found} profiles rather than five")]
145    ProfileCount {
146        /// Actual profile count.
147        found: usize,
148    },
149    /// Two records use the same exact selection.
150    #[error("duplicate profile selection {selection:?}")]
151    DuplicateSelection {
152        /// Repeated selection.
153        selection: ProfileSelection,
154    },
155    /// Two records use the same revisioned fact-bundle URN.
156    #[error("duplicate fact-bundle URN {urn}")]
157    DuplicateFactBundleUrn {
158        /// Repeated URN.
159        urn: &'static str,
160    },
161    /// The record does not enumerate every stable V1 fact id exactly once.
162    #[error("invalid fact inventory for {selection:?}")]
163    InvalidFactInventory {
164        /// Affected selection.
165        selection: ProfileSelection,
166    },
167    /// The accepted-input fact is not a nonempty canonical known-format set.
168    #[error("invalid accepted-input fact for {selection:?}")]
169    InvalidAcceptedInputFact {
170        /// Affected selection.
171        selection: ProfileSelection,
172    },
173    /// A setting descriptor id is repeated.
174    #[error("duplicate setting descriptor for {selection:?}")]
175    DuplicateSettingDescriptor {
176        /// Affected selection.
177        selection: ProfileSelection,
178    },
179    /// Applicability and default status contradict each other.
180    #[error("invalid default status for {setting:?} in {selection:?}")]
181    InvalidDescriptorDefault {
182        /// Affected selection.
183        selection: ProfileSelection,
184        /// Affected setting.
185        setting: SettingId,
186    },
187    /// A primary-source id is repeated within one profile.
188    #[error("duplicate source id {source_id} in {selection:?}")]
189    DuplicateSourceId {
190        /// Affected selection.
191        selection: ProfileSelection,
192        /// Repeated source id.
193        source_id: &'static str,
194    },
195    /// A source references a fact outside the record vocabulary.
196    #[error("source {source_id} references unknown fact {fact:?} in {selection:?}")]
197    UnknownSourceFact {
198        /// Affected selection.
199        selection: ProfileSelection,
200        /// Source id.
201        source_id: &'static str,
202        /// Unknown fact id.
203        fact: crate::FactId,
204    },
205    /// A source claims support for a fact whose record state is not known.
206    #[error("source {source_id} references non-known fact {fact:?} in {selection:?}")]
207    SourceReferencesNonKnownFact {
208        /// Affected selection.
209        selection: ProfileSelection,
210        /// Source id.
211        source_id: &'static str,
212        /// Fact without a known value.
213        fact: crate::FactId,
214    },
215    /// A source references a setting absent from the record.
216    #[error("source {source_id} references unknown setting {setting:?} in {selection:?}")]
217    UnknownSourceSetting {
218        /// Affected selection.
219        selection: ProfileSelection,
220        /// Source id.
221        source_id: &'static str,
222        /// Missing setting id.
223        setting: SettingId,
224    },
225    /// A known fact has no supporting primary-source reference.
226    #[error("known fact {fact:?} has no source reference in {selection:?}")]
227    UnreferencedKnownFact {
228        /// Affected selection.
229        selection: ProfileSelection,
230        /// Unsupported known fact.
231        fact: crate::FactId,
232    },
233    /// A descriptor has no supporting primary-source reference.
234    #[error("setting descriptor {setting:?} has no source reference in {selection:?}")]
235    UnreferencedSetting {
236        /// Affected selection.
237        selection: ProfileSelection,
238        /// Unsupported descriptor.
239        setting: SettingId,
240    },
241    /// The stored record identity differs from a fresh canonical encoding.
242    #[error("facts identity mismatch for {selection:?}")]
243    FactsIdentityMismatch {
244        /// Affected selection.
245        selection: ProfileSelection,
246    },
247}
248
249impl SettingValue {
250    pub(crate) const fn kind(&self) -> SettingValueKind {
251        match self {
252            SettingValue::Boolean(_) => SettingValueKind::Boolean,
253            SettingValue::BakeOrExtract(_) => SettingValueKind::BakeOrExtract,
254            SettingValue::SourceTransformPath(_) => SettingValueKind::SourceTransformPath,
255        }
256    }
257}