Skip to main content

oxilean_kernel/ffi/
types.rs

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