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