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}