nika_core/ast/analyzed/
ids.rs1use rustc_hash::FxHashMap;
8use std::fmt;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub struct TaskId(pub u32);
16
17impl TaskId {
18 pub const fn new(index: u32) -> Self {
20 Self(index)
21 }
22
23 pub const fn index(self) -> u32 {
25 self.0
26 }
27}
28
29impl fmt::Display for TaskId {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 write!(f, "task#{}", self.0)
32 }
33}
34
35#[derive(Debug, Clone, Default)]
40pub struct TaskTable {
41 names: Vec<String>,
43 index: FxHashMap<String, TaskId>,
45}
46
47impl TaskTable {
48 pub fn new() -> Self {
50 Self::default()
51 }
52
53 pub fn insert(&mut self, name: &str) -> TaskId {
57 let id = TaskId::new(self.names.len() as u32);
58 self.names.push(name.to_string());
59 self.index.insert(name.to_string(), id);
60 id
61 }
62
63 pub fn try_insert(&mut self, name: &str) -> Option<TaskId> {
67 if self.index.contains_key(name) {
68 None
69 } else {
70 Some(self.insert(name))
71 }
72 }
73
74 pub fn contains(&self, name: &str) -> bool {
76 self.index.contains_key(name)
77 }
78
79 pub fn get_id(&self, name: &str) -> Option<TaskId> {
81 self.index.get(name).copied()
82 }
83
84 pub fn get_name(&self, id: TaskId) -> Option<&str> {
86 self.names.get(id.0 as usize).map(|s| s.as_str())
87 }
88
89 pub fn len(&self) -> usize {
91 self.names.len()
92 }
93
94 pub fn is_empty(&self) -> bool {
96 self.names.is_empty()
97 }
98
99 pub fn iter(&self) -> impl Iterator<Item = (TaskId, &str)> {
101 self.names
102 .iter()
103 .enumerate()
104 .map(|(i, name)| (TaskId::new(i as u32), name.as_str()))
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_task_id() {
114 let id = TaskId::new(42);
115 assert_eq!(id.index(), 42);
116 assert_eq!(format!("{}", id), "task#42");
117 }
118
119 #[test]
120 fn test_task_table() {
121 let mut table = TaskTable::new();
122 assert!(table.is_empty());
123
124 let id1 = table.insert("task1");
125 let id2 = table.insert("task2");
126
127 assert_eq!(id1.index(), 0);
128 assert_eq!(id2.index(), 1);
129
130 assert_eq!(table.get_id("task1"), Some(id1));
131 assert_eq!(table.get_id("task2"), Some(id2));
132 assert_eq!(table.get_id("unknown"), None);
133
134 assert_eq!(table.get_name(id1), Some("task1"));
135 assert_eq!(table.get_name(id2), Some("task2"));
136 }
137
138 #[test]
139 fn test_task_table_iter() {
140 let mut table = TaskTable::new();
141 table.insert("a");
142 table.insert("b");
143 table.insert("c");
144
145 let pairs: Vec<_> = table.iter().collect();
146 assert_eq!(pairs.len(), 3);
147 assert_eq!(pairs[0].1, "a");
148 assert_eq!(pairs[1].1, "b");
149 assert_eq!(pairs[2].1, "c");
150 }
151
152 #[test]
153 fn test_task_table_try_insert() {
154 let mut table = TaskTable::new();
155
156 let id1 = table.try_insert("task1");
158 assert!(id1.is_some());
159 assert_eq!(id1.unwrap().index(), 0);
160
161 let id2 = table.try_insert("task2");
163 assert!(id2.is_some());
164 assert_eq!(id2.unwrap().index(), 1);
165
166 let id3 = table.try_insert("task1");
168 assert!(id3.is_none());
169
170 assert_eq!(table.len(), 2);
172 }
173
174 #[test]
175 fn test_task_table_contains() {
176 let mut table = TaskTable::new();
177 table.insert("existing");
178
179 assert!(table.contains("existing"));
180 assert!(!table.contains("missing"));
181 }
182
183 #[test]
184 fn test_task_table_o1_lookup() {
185 let mut table = TaskTable::new();
186 for i in 0..1000 {
188 table.insert(&format!("task_{}", i));
189 }
190
191 assert_eq!(table.get_id("task_500").map(|id| id.index()), Some(500));
193 assert_eq!(table.get_id("task_999").map(|id| id.index()), Some(999));
194 assert_eq!(table.get_id("nonexistent"), None);
195 }
196}