Skip to main content

rite_model/ir/
mod.rs

1//! Intermediate Representation (IR) for resolved ceremonies.
2//!
3//! The IR represents a ceremony after parsing and resolution:
4//! - All references have been validated
5//! - Execution order has been computed
6//! - Parameter values have been merged with defaults
7//! - Material sources have been validated (but not loaded)
8//!
9//! The IR is consumed by both the executor (for runtime) and
10//! script generation (for paper output).
11
12mod ceremony;
13mod ids;
14
15pub use ceremony::*;
16pub use ids::*;
17
18use indexmap::IndexMap;
19use std::hash::Hash;
20
21/// A symbol table mapping IDs to values.
22///
23/// Used for roles, sections, parameters, materials, and outputs.
24/// Provides O(1) lookup by ID and iteration in insertion order.
25#[derive(Debug, Clone)]
26pub struct SymbolTable<Id, T> {
27    items: IndexMap<Id, T>,
28}
29
30impl<Id: Eq + Hash + Clone, T> SymbolTable<Id, T> {
31    /// Create a new empty symbol table.
32    pub fn new() -> Self {
33        Self {
34            items: IndexMap::new(),
35        }
36    }
37
38    /// Insert an item, returning an error if the ID already exists.
39    pub fn insert(&mut self, id: Id, value: T) -> Result<(), Id> {
40        if self.items.contains_key(&id) {
41            return Err(id);
42        }
43        self.items.insert(id, value);
44        Ok(())
45    }
46
47    /// Insert an item, overwriting if it already exists.
48    pub fn insert_or_replace(&mut self, id: Id, value: T) {
49        self.items.insert(id, value);
50    }
51
52    /// Get an item by ID.
53    pub fn get(&self, id: &Id) -> Option<&T> {
54        self.items.get(id)
55    }
56
57    /// Get a mutable reference to an item by ID.
58    pub fn get_mut(&mut self, id: &Id) -> Option<&mut T> {
59        self.items.get_mut(id)
60    }
61
62    /// Check if an ID exists in the table.
63    pub fn contains(&self, id: &Id) -> bool {
64        self.items.contains_key(id)
65    }
66
67    /// Iterate over all (ID, value) pairs in insertion order.
68    pub fn iter(&self) -> impl Iterator<Item = (&Id, &T)> {
69        self.items.iter()
70    }
71
72    /// Iterate over all values in insertion order.
73    pub fn values(&self) -> impl Iterator<Item = &T> {
74        self.items.values()
75    }
76
77    /// Iterate over all IDs in insertion order.
78    pub fn keys(&self) -> impl Iterator<Item = &Id> {
79        self.items.keys()
80    }
81
82    /// Get the number of items.
83    pub fn len(&self) -> usize {
84        self.items.len()
85    }
86
87    /// Check if the table is empty.
88    pub fn is_empty(&self) -> bool {
89        self.items.is_empty()
90    }
91}
92
93impl<Id: Eq + Hash + Clone, T> Default for SymbolTable<Id, T> {
94    fn default() -> Self {
95        Self::new()
96    }
97}
98
99impl<Id: Eq + Hash + Clone, T> FromIterator<(Id, T)> for SymbolTable<Id, T> {
100    fn from_iter<I: IntoIterator<Item = (Id, T)>>(iter: I) -> Self {
101        Self {
102            items: iter.into_iter().collect(),
103        }
104    }
105}
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn symbol_table_basic_operations() {
113        let mut table: SymbolTable<RoleId, String> = SymbolTable::new();
114
115        // Insert succeeds for new ID.
116        assert!(table.insert(RoleId::new("admin"), "Alice".into()).is_ok());
117
118        // Insert fails for duplicate ID.
119        assert!(table.insert(RoleId::new("admin"), "Bob".into()).is_err());
120
121        // Get works.
122        assert_eq!(table.get(&RoleId::new("admin")), Some(&"Alice".to_string()));
123        assert_eq!(table.get(&RoleId::new("witness")), None);
124
125        // Contains works.
126        assert!(table.contains(&RoleId::new("admin")));
127        assert!(!table.contains(&RoleId::new("witness")));
128    }
129
130    #[test]
131    fn symbol_table_from_iterator() {
132        let table: SymbolTable<StepId, u32> = vec![
133            (StepId::new("step1"), 1),
134            (StepId::new("step2"), 2),
135            (StepId::new("step3"), 3),
136        ]
137        .into_iter()
138        .collect();
139
140        assert_eq!(table.len(), 3);
141        assert_eq!(table.get(&StepId::new("step2")), Some(&2));
142    }
143}