Skip to main content

sim_lib_serial_core/
error.rs

1//! Typed construction and validation failures.
2
3use crate::{AggregateRuleKind, AlphabetId, ProjectionId};
4use sim_lib_discrete_rank::RankAdapterError;
5use thiserror::Error;
6
7/// Failure while defining an alphabet or registering its stable identity.
8#[derive(Clone, Debug, PartialEq, Eq, Error)]
9pub enum AlphabetError {
10    /// An alphabet id was empty or did not use the stable portable syntax.
11    #[error("invalid alphabet id {value:?}: {reason}")]
12    InvalidId {
13        /// Rejected id text.
14        value: String,
15        /// Specific syntax violation.
16        reason: &'static str,
17    },
18    /// A finite musical alphabet contained no symbols.
19    #[error("alphabet {id} must contain at least one symbol")]
20    Empty {
21        /// Stable id of the empty alphabet.
22        id: AlphabetId,
23    },
24    /// The same symbol occupied two canonical alphabet positions.
25    #[error("alphabet {id} repeats positions {first} and {duplicate}")]
26    DuplicateSymbol {
27        /// Stable alphabet id.
28        id: AlphabetId,
29        /// First canonical position.
30        first: usize,
31        /// Repeated canonical position.
32        duplicate: usize,
33    },
34    /// A registry already contained the stable id being inserted.
35    #[error("duplicate alphabet id {0}")]
36    DuplicateId(AlphabetId),
37}
38
39/// Failure while compiling symbolic aggregate-rule data for one alphabet.
40#[derive(Clone, Debug, PartialEq, Eq, Error)]
41pub enum AggregateRuleError {
42    /// The alphabet itself was invalid.
43    #[error(transparent)]
44    Alphabet(#[from] AlphabetError),
45    /// A declaration named a symbol outside the alphabet.
46    #[error("aggregate declaration contains a foreign symbol for alphabet {alphabet_id}")]
47    ForeignSymbol {
48        /// Stable alphabet id.
49        alphabet_id: AlphabetId,
50    },
51    /// The same symbol was declared more than once.
52    #[error("aggregate declaration repeats alphabet position {position}")]
53    DuplicateDeclaration {
54        /// Canonical alphabet position identified from the supplied symbol.
55        position: usize,
56    },
57    /// A multiplicity declaration omitted an alphabet symbol.
58    #[error("aggregate declaration is missing alphabet position {position}")]
59    MissingDeclaration {
60        /// Canonical alphabet position without a declaration.
61        position: usize,
62    },
63    /// A multiplicity was zero where the selected rule requires presence.
64    #[error("aggregate declaration gives zero multiplicity at alphabet position {position}")]
65    ZeroMultiplicity {
66        /// Canonical alphabet position with zero multiplicity.
67        position: usize,
68    },
69    /// An omission rule named no omitted symbols.
70    #[error("declared-omissions rule must omit at least one symbol")]
71    NoOmissions,
72    /// A rule omitted every symbol and could not admit a musical series.
73    #[error("aggregate rule omits every symbol in alphabet {0}")]
74    OmitsEverything(AlphabetId),
75    /// Projected class identity did not use the stable portable syntax.
76    #[error("invalid projection id {value:?}: {reason}")]
77    InvalidProjectionId {
78        /// Rejected projection id text.
79        value: String,
80        /// Specific syntax violation.
81        reason: &'static str,
82    },
83    /// Two projected classes reused the same stable id.
84    #[error("duplicate projected class id {0}")]
85    DuplicateProjectionId(ProjectionId),
86    /// A projected class had no source symbols.
87    #[error("projected class {0} must contain at least one symbol")]
88    EmptyProjectionClass(ProjectionId),
89    /// An alphabet symbol was assigned to more than one projected class.
90    #[error("alphabet position {position} belongs to multiple projected classes")]
91    DuplicateProjectionMember {
92        /// Canonical alphabet position assigned twice.
93        position: usize,
94    },
95    /// An alphabet symbol was not assigned to a projected class.
96    #[error("alphabet position {position} has no projected class")]
97    MissingProjectionMember {
98        /// Canonical alphabet position without a class.
99        position: usize,
100    },
101    /// Declared multiplicities overflowed the platform series length.
102    #[error("aggregate multiplicity total exceeds the supported series length")]
103    MultiplicityOverflow,
104    /// The rule's declarations were compiled for another alphabet identity.
105    #[error("aggregate rule belongs to {rule_id}, not {series_id}")]
106    AlphabetMismatch {
107        /// Alphabet id retained by the rule.
108        rule_id: AlphabetId,
109        /// Alphabet id supplied to series construction.
110        series_id: AlphabetId,
111    },
112    /// The rule's declarations were compiled for another alphabet cardinality.
113    #[error("aggregate rule expects alphabet size {expected}, got {found}")]
114    CardinalityMismatch {
115        /// Cardinality retained by the rule.
116        expected: usize,
117        /// Cardinality supplied to series construction.
118        found: usize,
119    },
120}
121
122/// Failure while validating or ranking a symbol-bearing series.
123#[derive(Clone, Debug, PartialEq, Eq, Error)]
124pub enum SeriesError {
125    /// The supplied alphabet was invalid.
126    #[error(transparent)]
127    Alphabet(#[from] AlphabetError),
128    /// The aggregate rule was invalid for the supplied alphabet.
129    #[error(transparent)]
130    Rule(#[from] AggregateRuleError),
131    /// A series position contained a symbol outside the alphabet.
132    #[error("series position {position} is foreign to alphabet {alphabet_id}")]
133    ForeignSymbol {
134        /// Position in the supplied series order.
135        position: usize,
136        /// Stable alphabet id.
137        alphabet_id: AlphabetId,
138    },
139    /// A no-repeat rule encountered a repeated symbol.
140    #[error("series position {position} repeats position {first}")]
141    RepeatedSymbol {
142        /// Position of the repeated occurrence.
143        position: usize,
144        /// Position of the first occurrence.
145        first: usize,
146    },
147    /// Series length differed from the rule's declared total.
148    #[error("aggregate rule expects {expected} symbols, got {found}")]
149    WrongLength {
150        /// Required number of series positions.
151        expected: usize,
152        /// Supplied number of series positions.
153        found: usize,
154    },
155    /// An alphabet symbol occurred a different number of times than declared.
156    #[error("alphabet position {alphabet_position} occurs {found} times; expected {expected}")]
157    MultiplicityMismatch {
158        /// Canonical position of the affected symbol.
159        alphabet_position: usize,
160        /// Required number of occurrences.
161        expected: usize,
162        /// Observed number of occurrences.
163        found: usize,
164    },
165    /// A projected class occurred a different number of times than declared.
166    #[error("projected class {class_id} occurs {found} times; expected {expected}")]
167    ProjectionMismatch {
168        /// Stable projected class id.
169        class_id: ProjectionId,
170        /// Required number of occurrences.
171        expected: usize,
172        /// Observed number of occurrences.
173        found: usize,
174    },
175    /// The valid series is not an exactly-once permutation of its alphabet.
176    #[error("series is not an exactly-once permutation of alphabet {0}")]
177    NotPermutation(AlphabetId),
178    /// The shared discrete permutation-rank adapter rejected the request.
179    #[error(transparent)]
180    Rank(#[from] RankAdapterError),
181}
182
183/// Failure while validating or composing a finite ordinal map.
184#[derive(Clone, Debug, PartialEq, Eq, Error)]
185pub enum OrdinalMapError {
186    /// An ordinal selected a position outside the finite domain.
187    #[error("ordinal map output {output} selects input {input} outside 0..{cardinality}")]
188    OutOfRange {
189        /// Output position containing the invalid ordinal.
190        output: usize,
191        /// Rejected input position.
192        input: usize,
193        /// Finite domain cardinality.
194        cardinality: usize,
195    },
196    /// Two output positions selected the same input position.
197    #[error("ordinal map selects input {input} at outputs {first_output} and {duplicate_output}")]
198    DuplicateInput {
199        /// Repeated input position.
200        input: usize,
201        /// First output selecting it.
202        first_output: usize,
203        /// Later output selecting it.
204        duplicate_output: usize,
205    },
206    /// A map was applied to a slice with another cardinality.
207    #[error("ordinal map expects cardinality {expected}, got {found}")]
208    CardinalityMismatch {
209        /// Validated map cardinality.
210        expected: usize,
211        /// Supplied slice cardinality.
212        found: usize,
213    },
214    /// Two maps over different domains cannot be composed.
215    #[error("cannot compose ordinal maps of cardinalities {first} and {second}")]
216    CompositionCardinalityMismatch {
217        /// First map cardinality.
218        first: usize,
219        /// Second map cardinality.
220        second: usize,
221    },
222}
223
224/// Failure while validating an ordered block partition.
225#[derive(Clone, Debug, PartialEq, Eq, Error)]
226pub enum BlockPartitionError {
227    /// One declared block contained no positions.
228    #[error("block partition block {block} must not be empty")]
229    EmptyBlock {
230        /// Position of the empty block in declaration order.
231        block: usize,
232    },
233    /// Flattened blocks did not cover the declared finite domain.
234    #[error("block partition expects {expected} positions, got {found}")]
235    CardinalityMismatch {
236        /// Declared source cardinality.
237        expected: usize,
238        /// Number of declared positions.
239        found: usize,
240    },
241    /// Contiguous block lengths overflowed the platform cardinality.
242    #[error("block partition cardinality exceeds the supported series length")]
243    CardinalityOverflow,
244    /// The flattened block order was not a complete ordinal bijection.
245    #[error(transparent)]
246    OrdinalMap(#[from] OrdinalMapError),
247}
248
249/// Failure while validating a caller-supplied alphabet bijection.
250#[derive(Clone, Debug, PartialEq, Eq, Error)]
251pub enum SymbolBijectionError {
252    /// A source or target alphabet was invalid.
253    #[error(transparent)]
254    Alphabet(#[from] AlphabetError),
255    /// Source and target alphabets had different cardinalities.
256    #[error(
257        "symbol bijection source cardinality {source_cardinality} differs from target {target_cardinality}"
258    )]
259    CardinalityMismatch {
260        /// Source alphabet cardinality.
261        source_cardinality: usize,
262        /// Target alphabet or map cardinality.
263        target_cardinality: usize,
264    },
265    /// A supplied source symbol was outside the source alphabet.
266    #[error("symbol bijection contains a foreign source symbol for alphabet {alphabet_id}")]
267    ForeignSourceSymbol {
268        /// Source alphabet identity.
269        alphabet_id: AlphabetId,
270    },
271    /// A supplied target symbol was outside the target alphabet.
272    #[error("symbol bijection contains a foreign target symbol for alphabet {alphabet_id}")]
273    ForeignTargetSymbol {
274        /// Target alphabet identity.
275        alphabet_id: AlphabetId,
276    },
277    /// One source position was declared more than once.
278    #[error("symbol bijection repeats source position {position}")]
279    DuplicateSource {
280        /// Repeated source position.
281        position: usize,
282    },
283    /// Two source symbols selected the same target position.
284    #[error("symbol bijection repeats target position {position}")]
285    DuplicateTarget {
286        /// Repeated target position.
287        position: usize,
288    },
289    /// A source position had no mapping.
290    #[error("symbol bijection is missing source position {position}")]
291    MissingSource {
292        /// Unmapped source position.
293        position: usize,
294    },
295    /// A target position had no preimage.
296    #[error("symbol bijection is missing target position {position}")]
297    MissingTarget {
298        /// Unmapped target position.
299        position: usize,
300    },
301    /// The internal ordinal spelling was not a bijection.
302    #[error(transparent)]
303    OrdinalMap(#[from] OrdinalMapError),
304}
305
306/// Failure while applying, inverting, or composing a series transform.
307#[derive(Clone, Debug, PartialEq, Eq, Error)]
308pub enum SeriesTransformError {
309    /// A positional permutation was invalid for the series.
310    #[error(transparent)]
311    OrdinalMap(#[from] OrdinalMapError),
312    /// A symbolic relabeling was not a complete bijection.
313    #[error(transparent)]
314    Bijection(#[from] SymbolBijectionError),
315    /// Aggregate data could not be rebound to the target alphabet.
316    #[error(transparent)]
317    Rule(#[from] AggregateRuleError),
318    /// The transformed value did not satisfy its preserved aggregate rule.
319    #[error(transparent)]
320    Series(#[from] SeriesError),
321    /// An alphabet-bound relabeling was applied to another source alphabet.
322    #[error("transform expects source alphabet {expected}, got {found}")]
323    SourceAlphabetMismatch {
324        /// Alphabet bound into the relabeling.
325        expected: AlphabetId,
326        /// Alphabet retained by the supplied series.
327        found: AlphabetId,
328    },
329    /// Two alphabet relabelings did not meet at the same intermediate alphabet.
330    #[error("cannot compose relabeling to {first_target} with relabeling from {second_source}")]
331    CompositionAlphabetMismatch {
332        /// Target of the first relabeling.
333        first_target: AlphabetId,
334        /// Source of the second relabeling.
335        second_source: AlphabetId,
336    },
337    /// Private rule data disagreed with its public rule category.
338    #[error("aggregate rule data is missing for category {0:?}")]
339    RuleKindMismatch(AggregateRuleKind),
340}