Skip to main content

oxilean_kernel/error/
types.rs

1//! Auto-generated module
2//!
3//! 🤖 Generated with [SplitRS](https://github.com/cool-japan/splitrs)
4
5use crate::{Expr, Level, Name};
6
7use std::collections::HashMap;
8
9/// A counter that can measure elapsed time between snapshots.
10#[allow(dead_code)]
11pub struct Stopwatch {
12    start: crate::wall_clock::Instant,
13    splits: Vec<f64>,
14}
15#[allow(dead_code)]
16impl Stopwatch {
17    /// Creates and starts a new stopwatch.
18    pub fn start() -> Self {
19        Self {
20            start: crate::wall_clock::Instant::now(),
21            splits: Vec::new(),
22        }
23    }
24    /// Records a split time (elapsed since start).
25    pub fn split(&mut self) {
26        self.splits.push(self.elapsed_ms());
27    }
28    /// Returns total elapsed milliseconds since start.
29    pub fn elapsed_ms(&self) -> f64 {
30        self.start.elapsed().as_secs_f64() * 1000.0
31    }
32    /// Returns all recorded split times.
33    pub fn splits(&self) -> &[f64] {
34        &self.splits
35    }
36    /// Returns the number of splits.
37    pub fn num_splits(&self) -> usize {
38        self.splits.len()
39    }
40}
41/// A type-erased function pointer with arity tracking.
42#[allow(dead_code)]
43pub struct RawFnPtr {
44    /// The raw function pointer (stored as usize for type erasure).
45    ptr: usize,
46    arity: usize,
47    name: String,
48}
49#[allow(dead_code)]
50impl RawFnPtr {
51    /// Creates a new raw function pointer descriptor.
52    pub fn new(ptr: usize, arity: usize, name: impl Into<String>) -> Self {
53        Self {
54            ptr,
55            arity,
56            name: name.into(),
57        }
58    }
59    /// Returns the arity.
60    pub fn arity(&self) -> usize {
61        self.arity
62    }
63    /// Returns the name.
64    pub fn name(&self) -> &str {
65        &self.name
66    }
67    /// Returns the raw pointer value.
68    pub fn raw(&self) -> usize {
69        self.ptr
70    }
71}
72/// A pair of `StatSummary` values tracking before/after a transformation.
73#[allow(dead_code)]
74pub struct TransformStat {
75    before: StatSummary,
76    after: StatSummary,
77}
78#[allow(dead_code)]
79impl TransformStat {
80    /// Creates a new transform stat recorder.
81    pub fn new() -> Self {
82        Self {
83            before: StatSummary::new(),
84            after: StatSummary::new(),
85        }
86    }
87    /// Records a before value.
88    pub fn record_before(&mut self, v: f64) {
89        self.before.record(v);
90    }
91    /// Records an after value.
92    pub fn record_after(&mut self, v: f64) {
93        self.after.record(v);
94    }
95    /// Returns the mean reduction ratio (after/before).
96    pub fn mean_ratio(&self) -> Option<f64> {
97        let b = self.before.mean()?;
98        let a = self.after.mean()?;
99        if b.abs() < f64::EPSILON {
100            return None;
101        }
102        Some(a / b)
103    }
104}
105/// Represents a rewrite rule `lhs → rhs`.
106#[allow(dead_code)]
107#[allow(missing_docs)]
108pub struct RewriteRule {
109    /// The name of the rule.
110    pub name: String,
111    /// A string representation of the LHS pattern.
112    pub lhs: String,
113    /// A string representation of the RHS.
114    pub rhs: String,
115    /// Whether this is a conditional rule (has side conditions).
116    pub conditional: bool,
117}
118#[allow(dead_code)]
119impl RewriteRule {
120    /// Creates an unconditional rewrite rule.
121    pub fn unconditional(
122        name: impl Into<String>,
123        lhs: impl Into<String>,
124        rhs: impl Into<String>,
125    ) -> Self {
126        Self {
127            name: name.into(),
128            lhs: lhs.into(),
129            rhs: rhs.into(),
130            conditional: false,
131        }
132    }
133    /// Creates a conditional rewrite rule.
134    pub fn conditional(
135        name: impl Into<String>,
136        lhs: impl Into<String>,
137        rhs: impl Into<String>,
138    ) -> Self {
139        Self {
140            name: name.into(),
141            lhs: lhs.into(),
142            rhs: rhs.into(),
143            conditional: true,
144        }
145    }
146    /// Returns a textual representation.
147    pub fn display(&self) -> String {
148        format!("{}: {} → {}", self.name, self.lhs, self.rhs)
149    }
150}
151/// A generic counter that tracks min/max/sum for statistical summaries.
152#[allow(dead_code)]
153pub struct StatSummary {
154    count: u64,
155    sum: f64,
156    min: f64,
157    max: f64,
158}
159#[allow(dead_code)]
160impl StatSummary {
161    /// Creates an empty summary.
162    pub fn new() -> Self {
163        Self {
164            count: 0,
165            sum: 0.0,
166            min: f64::INFINITY,
167            max: f64::NEG_INFINITY,
168        }
169    }
170    /// Records a sample.
171    pub fn record(&mut self, val: f64) {
172        self.count += 1;
173        self.sum += val;
174        if val < self.min {
175            self.min = val;
176        }
177        if val > self.max {
178            self.max = val;
179        }
180    }
181    /// Returns the mean, or `None` if no samples.
182    pub fn mean(&self) -> Option<f64> {
183        if self.count == 0 {
184            None
185        } else {
186            Some(self.sum / self.count as f64)
187        }
188    }
189    /// Returns the minimum, or `None` if no samples.
190    pub fn min(&self) -> Option<f64> {
191        if self.count == 0 {
192            None
193        } else {
194            Some(self.min)
195        }
196    }
197    /// Returns the maximum, or `None` if no samples.
198    pub fn max(&self) -> Option<f64> {
199        if self.count == 0 {
200            None
201        } else {
202            Some(self.max)
203        }
204    }
205    /// Returns the count of recorded samples.
206    pub fn count(&self) -> u64 {
207        self.count
208    }
209}
210/// A mutable reference stack for tracking the current "focus" in a tree traversal.
211#[allow(dead_code)]
212pub struct FocusStack<T> {
213    items: Vec<T>,
214}
215#[allow(dead_code)]
216impl<T> FocusStack<T> {
217    /// Creates an empty focus stack.
218    pub fn new() -> Self {
219        Self { items: Vec::new() }
220    }
221    /// Focuses on `item`.
222    pub fn focus(&mut self, item: T) {
223        self.items.push(item);
224    }
225    /// Blurs (pops) the current focus.
226    pub fn blur(&mut self) -> Option<T> {
227        self.items.pop()
228    }
229    /// Returns the current focus, or `None`.
230    pub fn current(&self) -> Option<&T> {
231        self.items.last()
232    }
233    /// Returns the focus depth.
234    pub fn depth(&self) -> usize {
235        self.items.len()
236    }
237    /// Returns `true` if there is no current focus.
238    pub fn is_empty(&self) -> bool {
239        self.items.is_empty()
240    }
241}
242/// Errors that can occur during kernel type checking.
243#[derive(Clone, Debug)]
244pub enum KernelError {
245    /// Type mismatch: expected one type but got another
246    TypeMismatch {
247        /// Expected type
248        expected: Expr,
249        /// Actual type
250        got: Expr,
251        /// Context for the error
252        context: String,
253    },
254    /// Unbound variable (de Bruijn index out of range)
255    UnboundVariable(u32),
256    /// Unknown constant referenced
257    UnknownConstant(Name),
258    /// Universe level inconsistency
259    UniverseInconsistency {
260        /// Left-hand side level
261        lhs: Level,
262        /// Right-hand side level
263        rhs: Level,
264    },
265    /// Invalid inductive type declaration
266    InvalidInductive(String),
267    /// Invalid recursor
268    InvalidRecursor(String),
269    /// Expression is not a sort
270    NotASort(Expr),
271    /// Expression is not a function (Pi type)
272    NotAFunction(Expr),
273    /// Inductive type error
274    InductiveError(String),
275    /// Nested inductive types (an occurrence of the declared type under
276    /// another type constructor, e.g. `mk : List T → T`) are not yet
277    /// compiled to mutual inductives; the declaration is cleanly rejected
278    /// so a verifier can bucket it as "unsupported: nested inductives".
279    UnsupportedNestedInductive(Name),
280    /// Other error with description
281    Other(String),
282}
283impl KernelError {
284    /// Return the high-level category of this error.
285    pub fn category(&self) -> ErrorCategory {
286        match self {
287            KernelError::UnboundVariable(_) => ErrorCategory::Binding,
288            KernelError::UniverseInconsistency { .. } => ErrorCategory::Universe,
289            KernelError::TypeMismatch { .. }
290            | KernelError::NotASort(_)
291            | KernelError::NotAFunction(_) => ErrorCategory::TypeCheck,
292            KernelError::InvalidInductive(_)
293            | KernelError::InvalidRecursor(_)
294            | KernelError::InductiveError(_)
295            | KernelError::UnsupportedNestedInductive(_) => ErrorCategory::Inductive,
296            KernelError::UnknownConstant(_) => ErrorCategory::Resolution,
297            KernelError::Other(_) => ErrorCategory::Other,
298        }
299    }
300    /// Return `true` if this is a recoverable error.
301    pub fn is_recoverable(&self) -> bool {
302        matches!(
303            self,
304            KernelError::UnknownConstant(_) | KernelError::Other(_)
305        )
306    }
307    /// Return `true` if this error is fatal.
308    pub fn is_fatal(&self) -> bool {
309        !self.is_recoverable()
310    }
311    /// Attach a context string to the error.
312    pub fn with_context(self, ctx: impl Into<String>) -> Self {
313        match self {
314            KernelError::TypeMismatch {
315                expected,
316                got,
317                context,
318            } => {
319                let new_ctx = format!("{} > {}", ctx.into(), context);
320                KernelError::TypeMismatch {
321                    expected,
322                    got,
323                    context: new_ctx,
324                }
325            }
326            other => KernelError::Other(format!("{}: {}", ctx.into(), other)),
327        }
328    }
329    /// Convert this error into a short description.
330    pub fn short_description(&self) -> String {
331        match self {
332            KernelError::TypeMismatch { context, .. } => {
333                format!("type mismatch in {}", context)
334            }
335            KernelError::UnboundVariable(idx) => format!("unbound variable #{}", idx),
336            KernelError::UnknownConstant(name) => format!("unknown constant '{}'", name),
337            KernelError::UniverseInconsistency { .. } => "universe inconsistency".to_string(),
338            KernelError::InvalidInductive(msg) => format!("invalid inductive: {}", msg),
339            KernelError::InvalidRecursor(msg) => format!("invalid recursor: {}", msg),
340            KernelError::NotASort(_) => "expected a sort".to_string(),
341            KernelError::NotAFunction(_) => "expected a function type".to_string(),
342            KernelError::InductiveError(msg) => format!("inductive error: {}", msg),
343            KernelError::UnsupportedNestedInductive(name) => {
344                format!("unsupported: nested inductive type '{}'", name)
345            }
346            KernelError::Other(msg) => msg.clone(),
347        }
348    }
349    /// Convenience constructor: type mismatch.
350    pub fn type_mismatch(expected: Expr, got: Expr, context: impl Into<String>) -> Self {
351        KernelError::TypeMismatch {
352            expected,
353            got,
354            context: context.into(),
355        }
356    }
357    /// Convenience constructor: unknown constant.
358    pub fn unknown(name: Name) -> Self {
359        KernelError::UnknownConstant(name)
360    }
361    /// Convenience constructor: universe inconsistency.
362    pub fn universe(lhs: Level, rhs: Level) -> Self {
363        KernelError::UniverseInconsistency { lhs, rhs }
364    }
365}
366/// A collection of diagnostics.
367#[derive(Clone, Debug, Default)]
368pub struct DiagnosticCollection {
369    pub(super) diagnostics: Vec<Diagnostic>,
370}
371impl DiagnosticCollection {
372    /// Create an empty collection.
373    pub fn new() -> Self {
374        Self::default()
375    }
376    /// Add a diagnostic.
377    pub fn add(&mut self, d: Diagnostic) {
378        self.diagnostics.push(d);
379    }
380    /// Add from a kernel error.
381    pub fn add_error(&mut self, err: &KernelError) {
382        self.add(Diagnostic::from_error(err));
383    }
384    /// Return `true` if there are error-level diagnostics.
385    pub fn has_errors(&self) -> bool {
386        self.diagnostics
387            .iter()
388            .any(|d| d.severity == Severity::Error)
389    }
390    /// Return `true` if empty.
391    pub fn is_empty(&self) -> bool {
392        self.diagnostics.is_empty()
393    }
394    /// Number of diagnostics.
395    pub fn len(&self) -> usize {
396        self.diagnostics.len()
397    }
398    /// Iterate.
399    pub fn iter(&self) -> impl Iterator<Item = &Diagnostic> {
400        self.diagnostics.iter()
401    }
402    /// Filter by severity.
403    pub fn by_severity(&self, severity: Severity) -> Vec<&Diagnostic> {
404        self.diagnostics
405            .iter()
406            .filter(|d| d.severity == severity)
407            .collect()
408    }
409    /// Error-level diagnostics.
410    pub fn errors(&self) -> Vec<&Diagnostic> {
411        self.by_severity(Severity::Error)
412    }
413    /// Warning-level diagnostics.
414    pub fn warnings(&self) -> Vec<&Diagnostic> {
415        self.by_severity(Severity::Warning)
416    }
417}
418/// A non-empty list (at least one element guaranteed).
419#[allow(dead_code)]
420pub struct NonEmptyVec<T> {
421    head: T,
422    tail: Vec<T>,
423}
424#[allow(dead_code)]
425impl<T> NonEmptyVec<T> {
426    /// Creates a non-empty vec with a single element.
427    pub fn singleton(val: T) -> Self {
428        Self {
429            head: val,
430            tail: Vec::new(),
431        }
432    }
433    /// Pushes an element.
434    pub fn push(&mut self, val: T) {
435        self.tail.push(val);
436    }
437    /// Returns a reference to the first element.
438    pub fn first(&self) -> &T {
439        &self.head
440    }
441    /// Returns a reference to the last element.
442    pub fn last(&self) -> &T {
443        self.tail.last().unwrap_or(&self.head)
444    }
445    /// Returns the number of elements.
446    pub fn len(&self) -> usize {
447        1 + self.tail.len()
448    }
449    /// Returns whether the collection is empty.
450    pub fn is_empty(&self) -> bool {
451        self.len() == 0
452    }
453    /// Returns all elements as a Vec.
454    pub fn to_vec(&self) -> Vec<&T> {
455        let mut v = vec![&self.head];
456        v.extend(self.tail.iter());
457        v
458    }
459}
460/// A flat list of substitution pairs `(from, to)`.
461#[allow(dead_code)]
462pub struct FlatSubstitution {
463    pairs: Vec<(String, String)>,
464}
465#[allow(dead_code)]
466impl FlatSubstitution {
467    /// Creates an empty substitution.
468    pub fn new() -> Self {
469        Self { pairs: Vec::new() }
470    }
471    /// Adds a pair.
472    pub fn add(&mut self, from: impl Into<String>, to: impl Into<String>) {
473        self.pairs.push((from.into(), to.into()));
474    }
475    /// Applies all substitutions to `s` (leftmost-first order).
476    pub fn apply(&self, s: &str) -> String {
477        let mut result = s.to_string();
478        for (from, to) in &self.pairs {
479            result = result.replace(from.as_str(), to.as_str());
480        }
481        result
482    }
483    /// Returns the number of pairs.
484    pub fn len(&self) -> usize {
485        self.pairs.len()
486    }
487    /// Returns `true` if empty.
488    pub fn is_empty(&self) -> bool {
489        self.pairs.is_empty()
490    }
491}
492/// A label set for a graph node.
493#[allow(dead_code)]
494pub struct LabelSet {
495    labels: Vec<String>,
496}
497#[allow(dead_code)]
498impl LabelSet {
499    /// Creates a new empty label set.
500    pub fn new() -> Self {
501        Self { labels: Vec::new() }
502    }
503    /// Adds a label (deduplicates).
504    pub fn add(&mut self, label: impl Into<String>) {
505        let s = label.into();
506        if !self.labels.contains(&s) {
507            self.labels.push(s);
508        }
509    }
510    /// Returns `true` if `label` is present.
511    pub fn has(&self, label: &str) -> bool {
512        self.labels.iter().any(|l| l == label)
513    }
514    /// Returns the count of labels.
515    pub fn count(&self) -> usize {
516        self.labels.len()
517    }
518    /// Returns all labels.
519    pub fn all(&self) -> &[String] {
520        &self.labels
521    }
522}
523/// A sparse vector: stores only non-default elements.
524#[allow(dead_code)]
525pub struct SparseVec<T: Default + Clone + PartialEq> {
526    entries: std::collections::HashMap<usize, T>,
527    default_: T,
528    logical_len: usize,
529}
530#[allow(dead_code)]
531impl<T: Default + Clone + PartialEq> SparseVec<T> {
532    /// Creates a new sparse vector with logical length `len`.
533    pub fn new(len: usize) -> Self {
534        Self {
535            entries: std::collections::HashMap::new(),
536            default_: T::default(),
537            logical_len: len,
538        }
539    }
540    /// Sets element at `idx`.
541    pub fn set(&mut self, idx: usize, val: T) {
542        if val == self.default_ {
543            self.entries.remove(&idx);
544        } else {
545            self.entries.insert(idx, val);
546        }
547    }
548    /// Gets element at `idx`.
549    pub fn get(&self, idx: usize) -> &T {
550        self.entries.get(&idx).unwrap_or(&self.default_)
551    }
552    /// Returns the logical length.
553    pub fn len(&self) -> usize {
554        self.logical_len
555    }
556    /// Returns whether the collection is empty.
557    pub fn is_empty(&self) -> bool {
558        self.len() == 0
559    }
560    /// Returns the number of non-default elements.
561    pub fn nnz(&self) -> usize {
562        self.entries.len()
563    }
564}
565/// A simple key-value store backed by a sorted Vec for small maps.
566#[allow(dead_code)]
567pub struct SmallMap<K: Ord + Clone, V: Clone> {
568    entries: Vec<(K, V)>,
569}
570#[allow(dead_code)]
571impl<K: Ord + Clone, V: Clone> SmallMap<K, V> {
572    /// Creates a new empty small map.
573    pub fn new() -> Self {
574        Self {
575            entries: Vec::new(),
576        }
577    }
578    /// Inserts or replaces the value for `key`.
579    pub fn insert(&mut self, key: K, val: V) {
580        match self.entries.binary_search_by_key(&&key, |(k, _)| k) {
581            Ok(i) => self.entries[i].1 = val,
582            Err(i) => self.entries.insert(i, (key, val)),
583        }
584    }
585    /// Returns the value for `key`, or `None`.
586    pub fn get(&self, key: &K) -> Option<&V> {
587        self.entries
588            .binary_search_by_key(&key, |(k, _)| k)
589            .ok()
590            .map(|i| &self.entries[i].1)
591    }
592    /// Returns the number of entries.
593    pub fn len(&self) -> usize {
594        self.entries.len()
595    }
596    /// Returns `true` if empty.
597    pub fn is_empty(&self) -> bool {
598        self.entries.is_empty()
599    }
600    /// Returns all keys.
601    pub fn keys(&self) -> Vec<&K> {
602        self.entries.iter().map(|(k, _)| k).collect()
603    }
604    /// Returns all values.
605    pub fn values(&self) -> Vec<&V> {
606        self.entries.iter().map(|(_, v)| v).collect()
607    }
608}
609/// A fixed-size sliding window that computes a running sum.
610#[allow(dead_code)]
611pub struct SlidingSum {
612    window: Vec<f64>,
613    capacity: usize,
614    pos: usize,
615    sum: f64,
616    count: usize,
617}
618#[allow(dead_code)]
619impl SlidingSum {
620    /// Creates a sliding sum with the given window size.
621    pub fn new(capacity: usize) -> Self {
622        Self {
623            window: vec![0.0; capacity],
624            capacity,
625            pos: 0,
626            sum: 0.0,
627            count: 0,
628        }
629    }
630    /// Adds a value to the window, removing the oldest if full.
631    pub fn push(&mut self, val: f64) {
632        let oldest = self.window[self.pos];
633        self.sum -= oldest;
634        self.sum += val;
635        self.window[self.pos] = val;
636        self.pos = (self.pos + 1) % self.capacity;
637        if self.count < self.capacity {
638            self.count += 1;
639        }
640    }
641    /// Returns the current window sum.
642    pub fn sum(&self) -> f64 {
643        self.sum
644    }
645    /// Returns the window mean, or `None` if empty.
646    pub fn mean(&self) -> Option<f64> {
647        if self.count == 0 {
648            None
649        } else {
650            Some(self.sum / self.count as f64)
651        }
652    }
653    /// Returns the current window size (number of valid elements).
654    pub fn count(&self) -> usize {
655        self.count
656    }
657}
658/// A write-once cell.
659#[allow(dead_code)]
660pub struct WriteOnce<T> {
661    value: std::cell::Cell<Option<T>>,
662}
663#[allow(dead_code)]
664impl<T: Copy> WriteOnce<T> {
665    /// Creates an empty write-once cell.
666    pub fn new() -> Self {
667        Self {
668            value: std::cell::Cell::new(None),
669        }
670    }
671    /// Writes a value.  Returns `false` if already written.
672    pub fn write(&self, val: T) -> bool {
673        if self.value.get().is_some() {
674            return false;
675        }
676        self.value.set(Some(val));
677        true
678    }
679    /// Returns the value if written.
680    pub fn read(&self) -> Option<T> {
681        self.value.get()
682    }
683    /// Returns `true` if the value has been written.
684    pub fn is_written(&self) -> bool {
685        self.value.get().is_some()
686    }
687}
688/// A simple stack-based calculator for arithmetic expressions.
689#[allow(dead_code)]
690pub struct StackCalc {
691    stack: Vec<i64>,
692}
693#[allow(dead_code)]
694impl StackCalc {
695    /// Creates a new empty calculator.
696    pub fn new() -> Self {
697        Self { stack: Vec::new() }
698    }
699    /// Pushes an integer literal.
700    pub fn push(&mut self, n: i64) {
701        self.stack.push(n);
702    }
703    /// Replaces the top two values, `b` on top of `a`, with `op(a, b)` and
704    /// returns it.  Returns `None`, leaving the stack unchanged, if it holds
705    /// fewer than two values.
706    fn apply_binary(&mut self, op: impl FnOnce(i64, i64) -> i64) -> Option<i64> {
707        let (rest_len, a, b) = match self.stack[..] {
708            [ref rest @ .., a, b] => (rest.len(), a, b),
709            _ => return None,
710        };
711        let value = op(a, b);
712        self.stack.truncate(rest_len);
713        self.stack.push(value);
714        Some(value)
715    }
716    /// Adds the top two values and returns the sum, which replaces them.
717    /// Returns `None`, leaving the stack unchanged, if it holds fewer than
718    /// two values.
719    pub fn add(&mut self) -> Option<i64> {
720        self.apply_binary(|a, b| a + b)
721    }
722    /// Subtracts top from second and returns the difference, which replaces
723    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
724    /// than two values.
725    pub fn sub(&mut self) -> Option<i64> {
726        self.apply_binary(|a, b| a - b)
727    }
728    /// Multiplies the top two values and returns the product, which replaces
729    /// them.  Returns `None`, leaving the stack unchanged, if it holds fewer
730    /// than two values.
731    pub fn mul(&mut self) -> Option<i64> {
732        self.apply_binary(|a, b| a * b)
733    }
734    /// Peeks the top value.
735    pub fn peek(&self) -> Option<i64> {
736        self.stack.last().copied()
737    }
738    /// Returns the stack depth.
739    pub fn depth(&self) -> usize {
740        self.stack.len()
741    }
742}
743/// High-level category of a kernel error, useful for structured handling.
744#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
745pub enum ErrorCategory {
746    /// An error in variable binding / scoping
747    Binding,
748    /// A universe-level error
749    Universe,
750    /// A type-checking mismatch
751    TypeCheck,
752    /// An error in an inductive type or recursor definition
753    Inductive,
754    /// An unresolved reference to an unknown name
755    Resolution,
756    /// Any other kernel error
757    Other,
758}
759/// A phase label for grouping errors.
760#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
761pub enum KernelPhase {
762    /// Error during parsing.
763    Parse,
764    /// Error during elaboration.
765    Elab,
766    /// Error during kernel type-checking.
767    TypeCheck,
768    /// Error during reduction/evaluation.
769    Reduction,
770}
771/// A set of rewrite rules.
772#[allow(dead_code)]
773pub struct RewriteRuleSet {
774    rules: Vec<RewriteRule>,
775}
776#[allow(dead_code)]
777impl RewriteRuleSet {
778    /// Creates an empty rule set.
779    pub fn new() -> Self {
780        Self { rules: Vec::new() }
781    }
782    /// Adds a rule.
783    pub fn add(&mut self, rule: RewriteRule) {
784        self.rules.push(rule);
785    }
786    /// Returns the number of rules.
787    pub fn len(&self) -> usize {
788        self.rules.len()
789    }
790    /// Returns `true` if the set is empty.
791    pub fn is_empty(&self) -> bool {
792        self.rules.is_empty()
793    }
794    /// Returns all conditional rules.
795    pub fn conditional_rules(&self) -> Vec<&RewriteRule> {
796        self.rules.iter().filter(|r| r.conditional).collect()
797    }
798    /// Returns all unconditional rules.
799    pub fn unconditional_rules(&self) -> Vec<&RewriteRule> {
800        self.rules.iter().filter(|r| !r.conditional).collect()
801    }
802    /// Looks up a rule by name.
803    pub fn get(&self, name: &str) -> Option<&RewriteRule> {
804        self.rules.iter().find(|r| r.name == name)
805    }
806}
807/// A pool of reusable string buffers.
808#[allow(dead_code)]
809pub struct StringPool {
810    free: Vec<String>,
811}
812#[allow(dead_code)]
813impl StringPool {
814    /// Creates a new empty string pool.
815    pub fn new() -> Self {
816        Self { free: Vec::new() }
817    }
818    /// Takes a string from the pool (may be empty).
819    pub fn take(&mut self) -> String {
820        self.free.pop().unwrap_or_default()
821    }
822    /// Returns a string to the pool.
823    pub fn give(&mut self, mut s: String) {
824        s.clear();
825        self.free.push(s);
826    }
827    /// Returns the number of free strings in the pool.
828    pub fn free_count(&self) -> usize {
829        self.free.len()
830    }
831}
832/// A reusable scratch buffer for path computations.
833#[allow(dead_code)]
834pub struct PathBuf {
835    components: Vec<String>,
836}
837#[allow(dead_code)]
838impl PathBuf {
839    /// Creates a new empty path buffer.
840    pub fn new() -> Self {
841        Self {
842            components: Vec::new(),
843        }
844    }
845    /// Pushes a component.
846    pub fn push(&mut self, comp: impl Into<String>) {
847        self.components.push(comp.into());
848    }
849    /// Pops the last component.
850    pub fn pop(&mut self) {
851        self.components.pop();
852    }
853    /// Returns the current path as a `/`-separated string.
854    pub fn as_str(&self) -> String {
855        self.components.join("/")
856    }
857    /// Returns the depth of the path.
858    pub fn depth(&self) -> usize {
859        self.components.len()
860    }
861    /// Clears the path.
862    pub fn clear(&mut self) {
863        self.components.clear();
864    }
865}
866/// A dependency closure builder (transitive closure via BFS).
867#[allow(dead_code)]
868pub struct TransitiveClosure {
869    adj: Vec<Vec<usize>>,
870    n: usize,
871}
872#[allow(dead_code)]
873impl TransitiveClosure {
874    /// Creates a transitive closure builder for `n` nodes.
875    pub fn new(n: usize) -> Self {
876        Self {
877            adj: vec![Vec::new(); n],
878            n,
879        }
880    }
881    /// Adds a direct edge.
882    pub fn add_edge(&mut self, from: usize, to: usize) {
883        if from < self.n {
884            self.adj[from].push(to);
885        }
886    }
887    /// Computes all nodes reachable from `start` (including `start`).
888    pub fn reachable_from(&self, start: usize) -> Vec<usize> {
889        let mut visited = vec![false; self.n];
890        let mut queue = std::collections::VecDeque::new();
891        queue.push_back(start);
892        while let Some(node) = queue.pop_front() {
893            if node >= self.n || visited[node] {
894                continue;
895            }
896            visited[node] = true;
897            for &next in &self.adj[node] {
898                queue.push_back(next);
899            }
900        }
901        (0..self.n).filter(|&i| visited[i]).collect()
902    }
903    /// Returns `true` if `from` can transitively reach `to`.
904    pub fn can_reach(&self, from: usize, to: usize) -> bool {
905        self.reachable_from(from).contains(&to)
906    }
907}
908/// An annotated kernel error: primary error plus a list of notes.
909#[derive(Clone, Debug)]
910pub struct AnnotatedError {
911    /// The primary kernel error.
912    pub error: KernelError,
913    /// Attached notes.
914    pub notes: Vec<ErrorNote>,
915}
916impl AnnotatedError {
917    /// Create an annotated error from a kernel error.
918    pub fn new(error: KernelError) -> Self {
919        Self {
920            error,
921            notes: Vec::new(),
922        }
923    }
924    /// Attach a note.
925    pub fn with_note(mut self, note: ErrorNote) -> Self {
926        self.notes.push(note);
927        self
928    }
929    /// Number of notes.
930    pub fn num_notes(&self) -> usize {
931        self.notes.len()
932    }
933    /// Whether the primary error is fatal.
934    pub fn is_fatal(&self) -> bool {
935        self.error.is_fatal()
936    }
937}
938/// A kernel diagnostic: severity + message.
939#[derive(Clone, Debug)]
940pub struct Diagnostic {
941    /// Severity level.
942    pub severity: Severity,
943    /// Human-readable message.
944    pub message: String,
945    /// Optional location.
946    pub location: Option<Name>,
947}
948impl Diagnostic {
949    /// Create an error-level diagnostic.
950    pub fn error(message: impl Into<String>) -> Self {
951        Self {
952            severity: Severity::Error,
953            message: message.into(),
954            location: None,
955        }
956    }
957    /// Create a warning-level diagnostic.
958    pub fn warning(message: impl Into<String>) -> Self {
959        Self {
960            severity: Severity::Warning,
961            message: message.into(),
962            location: None,
963        }
964    }
965    /// Create an info-level diagnostic.
966    pub fn info(message: impl Into<String>) -> Self {
967        Self {
968            severity: Severity::Info,
969            message: message.into(),
970            location: None,
971        }
972    }
973    /// Attach a location.
974    pub fn at(mut self, loc: Name) -> Self {
975        self.location = Some(loc);
976        self
977    }
978    /// Create from a `KernelError`.
979    pub fn from_error(err: &KernelError) -> Self {
980        Self::error(err.to_string())
981    }
982}
983/// A single note attached to a kernel error, providing extra context.
984#[derive(Clone, Debug)]
985pub struct ErrorNote {
986    /// Note message.
987    pub message: String,
988    /// Optional location hint.
989    pub location: Option<Name>,
990}
991impl ErrorNote {
992    /// Create a note without location.
993    pub fn new(message: impl Into<String>) -> Self {
994        Self {
995            message: message.into(),
996            location: None,
997        }
998    }
999    /// Attach a location to this note.
1000    pub fn at(mut self, loc: Name) -> Self {
1001        self.location = Some(loc);
1002        self
1003    }
1004}
1005/// Severity of a kernel diagnostic.
1006#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
1007pub enum Severity {
1008    /// Informational.
1009    Info,
1010    /// Warning.
1011    Warning,
1012    /// Hard error.
1013    Error,
1014}
1015/// A versioned record that stores a history of values.
1016#[allow(dead_code)]
1017pub struct VersionedRecord<T: Clone> {
1018    /// Every earlier value, oldest first: version `n` is `earlier[n]`.
1019    earlier: Vec<T>,
1020    /// The latest value (version `earlier.len()`), declared last so it drops last.
1021    current: T,
1022}
1023#[allow(dead_code)]
1024impl<T: Clone> VersionedRecord<T> {
1025    /// Creates a new record with an initial value.
1026    pub fn new(initial: T) -> Self {
1027        Self {
1028            earlier: Vec::new(),
1029            current: initial,
1030        }
1031    }
1032    /// Updates the record with a new version.
1033    pub fn update(&mut self, val: T) {
1034        let previous = std::mem::replace(&mut self.current, val);
1035        self.earlier.push(previous);
1036    }
1037    /// Returns the current (latest) value.
1038    pub fn current(&self) -> &T {
1039        &self.current
1040    }
1041    /// Returns the value at version `n` (0-indexed), or `None`.
1042    pub fn at_version(&self, n: usize) -> Option<&T> {
1043        match self.earlier.get(n) {
1044            Some(value) => Some(value),
1045            None if n == self.earlier.len() => Some(&self.current),
1046            None => None,
1047        }
1048    }
1049    /// Returns the version number of the current value.
1050    pub fn version(&self) -> usize {
1051        self.earlier.len()
1052    }
1053    /// Returns `true` if more than one version exists.
1054    pub fn has_history(&self) -> bool {
1055        !self.earlier.is_empty()
1056    }
1057}
1058/// A simple directed acyclic graph.
1059#[allow(dead_code)]
1060pub struct SimpleDag {
1061    /// `edges[i]` is the list of direct successors of node `i`.
1062    edges: Vec<Vec<usize>>,
1063}
1064#[allow(dead_code)]
1065impl SimpleDag {
1066    /// Creates a DAG with `n` nodes and no edges.
1067    pub fn new(n: usize) -> Self {
1068        Self {
1069            edges: vec![Vec::new(); n],
1070        }
1071    }
1072    /// Adds an edge from `from` to `to`.
1073    pub fn add_edge(&mut self, from: usize, to: usize) {
1074        if from < self.edges.len() {
1075            self.edges[from].push(to);
1076        }
1077    }
1078    /// Returns the successors of `node`.
1079    pub fn successors(&self, node: usize) -> &[usize] {
1080        self.edges.get(node).map(|v| v.as_slice()).unwrap_or(&[])
1081    }
1082    /// Returns `true` if `from` can reach `to` via DFS.
1083    pub fn can_reach(&self, from: usize, to: usize) -> bool {
1084        let mut visited = vec![false; self.edges.len()];
1085        self.dfs(from, to, &mut visited)
1086    }
1087    fn dfs(&self, cur: usize, target: usize, visited: &mut Vec<bool>) -> bool {
1088        if cur == target {
1089            return true;
1090        }
1091        if cur >= visited.len() || visited[cur] {
1092            return false;
1093        }
1094        visited[cur] = true;
1095        for &next in self.successors(cur) {
1096            if self.dfs(next, target, visited) {
1097                return true;
1098            }
1099        }
1100        false
1101    }
1102    /// Returns the topological order of nodes, or `None` if a cycle is detected.
1103    pub fn topological_sort(&self) -> Option<Vec<usize>> {
1104        let n = self.edges.len();
1105        let mut in_degree = vec![0usize; n];
1106        for succs in &self.edges {
1107            for &s in succs {
1108                if s < n {
1109                    in_degree[s] += 1;
1110                }
1111            }
1112        }
1113        let mut queue: std::collections::VecDeque<usize> =
1114            (0..n).filter(|&i| in_degree[i] == 0).collect();
1115        let mut order = Vec::new();
1116        while let Some(node) = queue.pop_front() {
1117            order.push(node);
1118            for &s in self.successors(node) {
1119                if s < n {
1120                    in_degree[s] -= 1;
1121                    if in_degree[s] == 0 {
1122                        queue.push_back(s);
1123                    }
1124                }
1125            }
1126        }
1127        if order.len() == n {
1128            Some(order)
1129        } else {
1130            None
1131        }
1132    }
1133    /// Returns the number of nodes.
1134    pub fn num_nodes(&self) -> usize {
1135        self.edges.len()
1136    }
1137}
1138/// A tagged union for representing a simple two-case discriminated union.
1139#[allow(dead_code)]
1140pub enum Either2<A, B> {
1141    /// The first alternative.
1142    First(A),
1143    /// The second alternative.
1144    Second(B),
1145}
1146#[allow(dead_code)]
1147impl<A, B> Either2<A, B> {
1148    /// Returns `true` if this is the first alternative.
1149    pub fn is_first(&self) -> bool {
1150        matches!(self, Either2::First(_))
1151    }
1152    /// Returns `true` if this is the second alternative.
1153    pub fn is_second(&self) -> bool {
1154        matches!(self, Either2::Second(_))
1155    }
1156    /// Returns the first value if present.
1157    pub fn first(self) -> Option<A> {
1158        match self {
1159            Either2::First(a) => Some(a),
1160            _ => None,
1161        }
1162    }
1163    /// Returns the second value if present.
1164    pub fn second(self) -> Option<B> {
1165        match self {
1166            Either2::Second(b) => Some(b),
1167            _ => None,
1168        }
1169    }
1170    /// Maps over the first alternative.
1171    pub fn map_first<C, F: FnOnce(A) -> C>(self, f: F) -> Either2<C, B> {
1172        match self {
1173            Either2::First(a) => Either2::First(f(a)),
1174            Either2::Second(b) => Either2::Second(b),
1175        }
1176    }
1177}
1178/// An accumulator that collects multiple kernel errors.
1179#[derive(Clone, Debug, Default)]
1180pub struct ErrorAccumulator {
1181    pub(super) errors: Vec<KernelError>,
1182}
1183impl ErrorAccumulator {
1184    /// Create a new, empty accumulator.
1185    pub fn new() -> Self {
1186        Self::default()
1187    }
1188    /// Push a new error.
1189    pub fn push(&mut self, err: KernelError) {
1190        self.errors.push(err);
1191    }
1192    /// Return `true` if empty.
1193    pub fn is_empty(&self) -> bool {
1194        self.errors.is_empty()
1195    }
1196    /// Return the number of errors.
1197    pub fn len(&self) -> usize {
1198        self.errors.len()
1199    }
1200    /// Iterate over errors.
1201    pub fn iter(&self) -> impl Iterator<Item = &KernelError> {
1202        self.errors.iter()
1203    }
1204    /// Drain all errors.
1205    pub fn drain(&mut self) -> Vec<KernelError> {
1206        std::mem::take(&mut self.errors)
1207    }
1208    /// Convert to a `Result`.
1209    #[allow(clippy::result_large_err)]
1210    pub fn into_result(mut self) -> Result<(), KernelError> {
1211        if self.errors.is_empty() {
1212            Ok(())
1213        } else {
1214            Err(self.errors.remove(0))
1215        }
1216    }
1217    /// Count errors by category.
1218    pub fn count_by_category(&self) -> std::collections::HashMap<ErrorCategory, usize> {
1219        let mut counts = std::collections::HashMap::new();
1220        for err in &self.errors {
1221            *counts.entry(err.category()).or_insert(0) += 1;
1222        }
1223        counts
1224    }
1225    /// Return only fatal errors.
1226    pub fn fatal_errors(&self) -> Vec<&KernelError> {
1227        self.errors.iter().filter(|e| e.is_fatal()).collect()
1228    }
1229    /// Return only recoverable errors.
1230    pub fn recoverable_errors(&self) -> Vec<&KernelError> {
1231        self.errors.iter().filter(|e| e.is_recoverable()).collect()
1232    }
1233}
1234/// A phased error: kernel error associated with a compilation phase.
1235#[derive(Clone, Debug)]
1236pub struct PhasedError {
1237    /// The phase in which the error occurred.
1238    pub phase: KernelPhase,
1239    /// The underlying kernel error.
1240    pub error: KernelError,
1241}
1242impl PhasedError {
1243    /// Create a phased error.
1244    pub fn new(phase: KernelPhase, error: KernelError) -> Self {
1245        Self { phase, error }
1246    }
1247}
1248/// A hierarchical configuration tree.
1249#[allow(dead_code)]
1250pub struct ConfigNode {
1251    key: String,
1252    value: Option<String>,
1253    children: Vec<ConfigNode>,
1254}
1255#[allow(dead_code)]
1256impl ConfigNode {
1257    /// Creates a leaf config node with a value.
1258    pub fn leaf(key: impl Into<String>, value: impl Into<String>) -> Self {
1259        Self {
1260            key: key.into(),
1261            value: Some(value.into()),
1262            children: Vec::new(),
1263        }
1264    }
1265    /// Creates a section node with children.
1266    pub fn section(key: impl Into<String>) -> Self {
1267        Self {
1268            key: key.into(),
1269            value: None,
1270            children: Vec::new(),
1271        }
1272    }
1273    /// Adds a child node.
1274    pub fn add_child(&mut self, child: ConfigNode) {
1275        self.children.push(child);
1276    }
1277    /// Returns the key.
1278    pub fn key(&self) -> &str {
1279        &self.key
1280    }
1281    /// Returns the value, or `None` for section nodes.
1282    pub fn value(&self) -> Option<&str> {
1283        self.value.as_deref()
1284    }
1285    /// Returns the number of children.
1286    pub fn num_children(&self) -> usize {
1287        self.children.len()
1288    }
1289    /// Looks up a dot-separated path.
1290    pub fn lookup(&self, path: &str) -> Option<&str> {
1291        let mut parts = path.splitn(2, '.');
1292        let head = parts.next()?;
1293        let tail = parts.next();
1294        if head != self.key {
1295            return None;
1296        }
1297        match tail {
1298            None => self.value.as_deref(),
1299            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1300        }
1301    }
1302    fn lookup_relative(&self, path: &str) -> Option<&str> {
1303        let mut parts = path.splitn(2, '.');
1304        let head = parts.next()?;
1305        let tail = parts.next();
1306        if head != self.key {
1307            return None;
1308        }
1309        match tail {
1310            None => self.value.as_deref(),
1311            Some(rest) => self.children.iter().find_map(|c| c.lookup_relative(rest)),
1312        }
1313    }
1314}
1315/// A token bucket rate limiter.
1316#[allow(dead_code)]
1317pub struct TokenBucket {
1318    capacity: u64,
1319    tokens: u64,
1320    refill_per_ms: u64,
1321    last_refill: crate::wall_clock::Instant,
1322}
1323#[allow(dead_code)]
1324impl TokenBucket {
1325    /// Creates a new token bucket.
1326    pub fn new(capacity: u64, refill_per_ms: u64) -> Self {
1327        Self {
1328            capacity,
1329            tokens: capacity,
1330            refill_per_ms,
1331            last_refill: crate::wall_clock::Instant::now(),
1332        }
1333    }
1334    /// Attempts to consume `n` tokens.  Returns `true` on success.
1335    pub fn try_consume(&mut self, n: u64) -> bool {
1336        self.refill();
1337        if self.tokens >= n {
1338            self.tokens -= n;
1339            true
1340        } else {
1341            false
1342        }
1343    }
1344    fn refill(&mut self) {
1345        let now = crate::wall_clock::Instant::now();
1346        let elapsed_ms = now.duration_since(self.last_refill).as_millis() as u64;
1347        if elapsed_ms > 0 {
1348            let new_tokens = elapsed_ms * self.refill_per_ms;
1349            self.tokens = (self.tokens + new_tokens).min(self.capacity);
1350            self.last_refill = now;
1351        }
1352    }
1353    /// Returns the number of currently available tokens.
1354    pub fn available(&self) -> u64 {
1355        self.tokens
1356    }
1357    /// Returns the bucket capacity.
1358    pub fn capacity(&self) -> u64 {
1359        self.capacity
1360    }
1361}
1362/// A min-heap implemented as a binary heap.
1363#[allow(dead_code)]
1364pub struct MinHeap<T: Ord> {
1365    data: Vec<T>,
1366}
1367#[allow(dead_code)]
1368impl<T: Ord> MinHeap<T> {
1369    /// Creates a new empty min-heap.
1370    pub fn new() -> Self {
1371        Self { data: Vec::new() }
1372    }
1373    /// Inserts an element.
1374    pub fn push(&mut self, val: T) {
1375        self.data.push(val);
1376        self.sift_up(self.data.len() - 1);
1377    }
1378    /// Removes and returns the minimum element.
1379    pub fn pop(&mut self) -> Option<T> {
1380        if self.data.is_empty() {
1381            return None;
1382        }
1383        let n = self.data.len();
1384        self.data.swap(0, n - 1);
1385        let min = self.data.pop();
1386        if !self.data.is_empty() {
1387            self.sift_down(0);
1388        }
1389        min
1390    }
1391    /// Returns a reference to the minimum element.
1392    pub fn peek(&self) -> Option<&T> {
1393        self.data.first()
1394    }
1395    /// Returns the number of elements.
1396    pub fn len(&self) -> usize {
1397        self.data.len()
1398    }
1399    /// Returns `true` if empty.
1400    pub fn is_empty(&self) -> bool {
1401        self.data.is_empty()
1402    }
1403    fn sift_up(&mut self, mut i: usize) {
1404        while i > 0 {
1405            let parent = (i - 1) / 2;
1406            if self.data[i] < self.data[parent] {
1407                self.data.swap(i, parent);
1408                i = parent;
1409            } else {
1410                break;
1411            }
1412        }
1413    }
1414    fn sift_down(&mut self, mut i: usize) {
1415        let n = self.data.len();
1416        loop {
1417            let left = 2 * i + 1;
1418            let right = 2 * i + 2;
1419            let mut smallest = i;
1420            if left < n && self.data[left] < self.data[smallest] {
1421                smallest = left;
1422            }
1423            if right < n && self.data[right] < self.data[smallest] {
1424                smallest = right;
1425            }
1426            if smallest == i {
1427                break;
1428            }
1429            self.data.swap(i, smallest);
1430            i = smallest;
1431        }
1432    }
1433}
1434/// A structured error report.
1435#[derive(Clone, Debug)]
1436pub struct ErrorReport {
1437    /// The primary error
1438    pub primary: KernelError,
1439    /// Optional source location hint
1440    pub location: Option<Name>,
1441    /// Optional supplementary notes
1442    pub notes: Vec<String>,
1443}
1444impl ErrorReport {
1445    /// Create a report from a kernel error.
1446    pub fn new(primary: KernelError) -> Self {
1447        Self {
1448            primary,
1449            location: None,
1450            notes: vec![],
1451        }
1452    }
1453    /// Attach a location.
1454    pub fn with_location(mut self, loc: Name) -> Self {
1455        self.location = Some(loc);
1456        self
1457    }
1458    /// Add a note.
1459    pub fn add_note(mut self, note: impl Into<String>) -> Self {
1460        self.notes.push(note.into());
1461        self
1462    }
1463    /// Category of the primary error.
1464    pub fn category(&self) -> ErrorCategory {
1465        self.primary.category()
1466    }
1467    /// Is the primary error recoverable?
1468    pub fn is_recoverable(&self) -> bool {
1469        self.primary.is_recoverable()
1470    }
1471}
1472/// A builder for constructing detailed error messages.
1473#[derive(Clone, Debug, Default)]
1474pub struct ErrorContext {
1475    frames: Vec<String>,
1476}
1477impl ErrorContext {
1478    /// Create an empty context.
1479    pub fn new() -> Self {
1480        Self::default()
1481    }
1482    /// Push a frame.
1483    pub fn push(mut self, frame: impl Into<String>) -> Self {
1484        self.frames.push(frame.into());
1485        self
1486    }
1487    /// Format as a backtrace string.
1488    pub fn format(&self) -> String {
1489        self.frames
1490            .iter()
1491            .enumerate()
1492            .map(|(i, f)| format!("  #{} {}", i, f))
1493            .collect::<Vec<_>>()
1494            .join("\n")
1495    }
1496    /// Build a `KernelError::Other` that includes context.
1497    pub fn into_error(self, msg: impl Into<String>) -> KernelError {
1498        if self.frames.is_empty() {
1499            KernelError::Other(msg.into())
1500        } else {
1501            KernelError::Other(format!("{}\nContext:\n{}", msg.into(), self.format()))
1502        }
1503    }
1504}
1505/// A simple decision tree node for rule dispatching.
1506#[allow(dead_code)]
1507#[allow(missing_docs)]
1508pub enum DecisionNode {
1509    /// A leaf with an action string.
1510    Leaf(String),
1511    /// An interior node: check `key` equals `val` → `yes_branch`, else `no_branch`.
1512    Branch {
1513        key: String,
1514        val: String,
1515        yes_branch: Box<DecisionNode>,
1516        no_branch: Box<DecisionNode>,
1517    },
1518}
1519#[allow(dead_code)]
1520impl DecisionNode {
1521    /// Evaluates the decision tree with the given context.
1522    pub fn evaluate(&self, ctx: &std::collections::HashMap<String, String>) -> &str {
1523        match self {
1524            DecisionNode::Leaf(action) => action.as_str(),
1525            DecisionNode::Branch {
1526                key,
1527                val,
1528                yes_branch,
1529                no_branch,
1530            } => {
1531                let actual = ctx.get(key).map(|s| s.as_str()).unwrap_or("");
1532                if actual == val.as_str() {
1533                    yes_branch.evaluate(ctx)
1534                } else {
1535                    no_branch.evaluate(ctx)
1536                }
1537            }
1538        }
1539    }
1540    /// Returns the depth of the decision tree.
1541    pub fn depth(&self) -> usize {
1542        match self {
1543            DecisionNode::Leaf(_) => 0,
1544            DecisionNode::Branch {
1545                yes_branch,
1546                no_branch,
1547                ..
1548            } => 1 + yes_branch.depth().max(no_branch.depth()),
1549        }
1550    }
1551}
1552/// A trie-based prefix counter.
1553#[allow(dead_code)]
1554pub struct PrefixCounter {
1555    children: std::collections::HashMap<char, PrefixCounter>,
1556    count: usize,
1557}
1558#[allow(dead_code)]
1559impl PrefixCounter {
1560    /// Creates an empty prefix counter.
1561    pub fn new() -> Self {
1562        Self {
1563            children: std::collections::HashMap::new(),
1564            count: 0,
1565        }
1566    }
1567    /// Records a string.
1568    pub fn record(&mut self, s: &str) {
1569        self.count += 1;
1570        let mut node = self;
1571        for c in s.chars() {
1572            node = node.children.entry(c).or_default();
1573            node.count += 1;
1574        }
1575    }
1576    /// Returns how many strings have been recorded that start with `prefix`.
1577    pub fn count_with_prefix(&self, prefix: &str) -> usize {
1578        let mut node = self;
1579        for c in prefix.chars() {
1580            match node.children.get(&c) {
1581                Some(n) => node = n,
1582                None => return 0,
1583            }
1584        }
1585        node.count
1586    }
1587}
1588/// A window iterator that yields overlapping windows of size `n`.
1589#[allow(dead_code)]
1590pub struct WindowIterator<'a, T> {
1591    pub(super) data: &'a [T],
1592    pub(super) pos: usize,
1593    pub(super) window: usize,
1594}
1595#[allow(dead_code)]
1596impl<'a, T> WindowIterator<'a, T> {
1597    /// Creates a new window iterator.
1598    pub fn new(data: &'a [T], window: usize) -> Self {
1599        Self {
1600            data,
1601            pos: 0,
1602            window,
1603        }
1604    }
1605}
1606/// A simple mutable key-value store for test fixtures.
1607#[allow(dead_code)]
1608pub struct Fixture {
1609    data: std::collections::HashMap<String, String>,
1610}
1611#[allow(dead_code)]
1612impl Fixture {
1613    /// Creates an empty fixture.
1614    pub fn new() -> Self {
1615        Self {
1616            data: std::collections::HashMap::new(),
1617        }
1618    }
1619    /// Sets a key.
1620    pub fn set(&mut self, key: impl Into<String>, val: impl Into<String>) {
1621        self.data.insert(key.into(), val.into());
1622    }
1623    /// Gets a value.
1624    pub fn get(&self, key: &str) -> Option<&str> {
1625        self.data.get(key).map(|s| s.as_str())
1626    }
1627    /// Returns the number of entries.
1628    pub fn len(&self) -> usize {
1629        self.data.len()
1630    }
1631    /// Returns whether the collection is empty.
1632    pub fn is_empty(&self) -> bool {
1633        self.len() == 0
1634    }
1635}