formualizer_eval/engine/
sheet_registry.rs1use std::collections::HashMap;
2
3use crate::SheetId;
4
5#[derive(Default, Debug)]
6pub struct SheetRegistry {
7 id_by_name: HashMap<String, SheetId>,
8 name_by_id: Vec<String>,
9}
10
11impl SheetRegistry {
12 pub fn new() -> Self {
13 SheetRegistry::default()
14 }
15
16 pub fn id_for(&mut self, name: &str) -> SheetId {
17 let key = name.to_lowercase();
20 if let Some(&id) = self.id_by_name.get(&key) {
21 return id;
22 }
23
24 let id = self.name_by_id.len() as SheetId;
25 self.name_by_id.push(name.to_string());
26 self.id_by_name.insert(key, id);
27 id
28 }
29
30 pub fn name(&self, id: SheetId) -> &str {
31 if (id as usize) < self.name_by_id.len() {
32 &self.name_by_id[id as usize]
33 } else {
34 ""
35 }
36 }
37
38 pub fn get_id(&self, name: &str) -> Option<SheetId> {
39 self.id_by_name.get(&name.to_lowercase()).copied()
41 }
42
43 pub fn active_len(&self) -> usize {
45 self.name_by_id
46 .iter()
47 .filter(|name| !name.is_empty())
48 .count()
49 }
50
51 pub fn active_position_by_id(&self, id: SheetId) -> Option<usize> {
53 let idx = id as usize;
54 if idx >= self.name_by_id.len() || self.name_by_id[idx].is_empty() {
55 return None;
56 }
57 Some(
58 self.name_by_id
59 .iter()
60 .take(idx + 1)
61 .filter(|name| !name.is_empty())
62 .count(),
63 )
64 }
65
66 pub fn active_position(&self, name: &str) -> Option<usize> {
68 self.get_id(name)
69 .and_then(|id| self.active_position_by_id(id))
70 }
71
72 pub fn active_span_len(&self, first: &str, last: &str) -> Option<usize> {
74 let a = self.active_position(first)?;
75 let b = self.active_position(last)?;
76 Some(a.abs_diff(b) + 1)
77 }
78
79 pub fn all_sheets(&self) -> Vec<(SheetId, String)> {
81 self.name_by_id
82 .iter()
83 .enumerate()
84 .filter(|(_, name)| !name.is_empty())
85 .map(|(id, name)| (id as SheetId, name.clone()))
86 .collect()
87 }
88
89 pub fn remove(&mut self, id: SheetId) -> Result<(), formualizer_common::ExcelError> {
92 use formualizer_common::{ExcelError, ExcelErrorKind};
93
94 if id as usize >= self.name_by_id.len() {
96 return Err(
97 ExcelError::new(ExcelErrorKind::Value).with_message("Sheet ID does not exist")
98 );
99 }
100
101 let name = self.name_by_id[id as usize].clone();
103 if name.is_empty() {
104 return Ok(());
106 }
107
108 self.id_by_name.remove(&name.to_lowercase());
110
111 self.name_by_id[id as usize] = String::new();
113
114 Ok(())
115 }
116
117 pub fn rename(
119 &mut self,
120 id: SheetId,
121 new_name: &str,
122 ) -> Result<(), formualizer_common::ExcelError> {
123 use formualizer_common::{ExcelError, ExcelErrorKind};
124
125 if id as usize >= self.name_by_id.len() {
127 return Err(
128 ExcelError::new(ExcelErrorKind::Value).with_message("Sheet ID does not exist")
129 );
130 }
131
132 let old_name = self.name_by_id[id as usize].clone();
134
135 if let Some(&existing_id) = self.id_by_name.get(&new_name.to_lowercase())
137 && existing_id != id
138 {
139 return Err(ExcelError::new(ExcelErrorKind::Value)
140 .with_message(format!("Sheet name '{new_name}' already exists")));
141 }
142
143 self.id_by_name.remove(&old_name.to_lowercase());
145
146 self.name_by_id[id as usize] = new_name.to_string();
148 self.id_by_name.insert(new_name.to_lowercase(), id);
149
150 Ok(())
151 }
152}
153
154#[cfg(test)]
155mod tests {
156 use super::*;
157
158 #[test]
159 fn sheet_names_are_case_insensitive() {
160 let mut reg = SheetRegistry::new();
161 let id = reg.id_for("CONFIG");
162 assert_eq!(reg.get_id("CONFIG"), Some(id));
164 assert_eq!(reg.get_id("Config"), Some(id));
165 assert_eq!(reg.get_id("config"), Some(id));
166 assert_eq!(reg.id_for("Config"), id);
168 assert_eq!(reg.name(id), "CONFIG");
170 }
171}