Skip to main content

acadrust/tables/
mod.rs

1//! Table types and the generic [`Table`] container.
2//!
3//! Tables store named, reusable definitions that entities reference:
4//!
5//! | Type | Purpose |
6//! |------|----------|
7//! | [`Layer`] | Drawing layers (color, linetype, visibility) |
8//! | [`LineType`] | Dash patterns |
9//! | [`TextStyle`] | Font / text formatting |
10//! | [`DimStyle`] | Dimension appearance |
11//! | [`BlockRecord`] | Block definition registry |
12//! | [`AppId`] | Application identifier (XData) |
13//! | [`View`] | Named view configurations |
14//! | [`VPort`] | Viewport configurations |
15//! | [`Ucs`] | User coordinate systems |
16
17use crate::types::Handle;
18use indexmap::IndexMap;
19
20pub mod layer;
21pub mod linetype;
22pub mod textstyle;
23pub mod block_record;
24pub mod dimstyle;
25pub mod appid;
26pub mod view;
27pub mod vport;
28pub mod ucs;
29
30pub use layer::{Layer, LayerFlags};
31pub use linetype::{LineType, LineTypeElement};
32pub use textstyle::{TextStyle, TextGenerationFlags};
33pub use block_record::BlockRecord;
34pub use dimstyle::DimStyle;
35pub use appid::AppId;
36pub use view::View;
37pub use vport::VPort;
38pub use ucs::Ucs;
39
40/// Base trait for all table entries
41pub trait TableEntry {
42    /// Get the entry's unique handle
43    fn handle(&self) -> Handle;
44
45    /// Set the entry's handle
46    fn set_handle(&mut self, handle: Handle);
47
48    /// Get the entry's name
49    fn name(&self) -> &str;
50
51    /// Set the entry's name
52    fn set_name(&mut self, name: String);
53
54    /// Check if this is a standard/default entry
55    fn is_standard(&self) -> bool {
56        false
57    }
58}
59
60/// Generic table for storing named entries
61#[derive(Debug, Clone, PartialEq)]
62#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub struct Table<T: TableEntry> {
64    /// Entries stored by name (case-insensitive)
65    entries: IndexMap<String, T>,
66    /// Table handle
67    handle: Handle,
68}
69
70impl<T: TableEntry> Table<T> {
71    /// Create a new empty table
72    pub fn new() -> Self {
73        Table {
74            entries: IndexMap::new(),
75            handle: Handle::NULL,
76        }
77    }
78
79    /// Create a table with a specific handle
80    pub fn with_handle(handle: Handle) -> Self {
81        Table {
82            entries: IndexMap::new(),
83            handle,
84        }
85    }
86
87    /// Get the table's handle
88    pub fn handle(&self) -> Handle {
89        self.handle
90    }
91
92    /// Set the table's handle
93    pub fn set_handle(&mut self, handle: Handle) {
94        self.handle = handle;
95    }
96
97    /// Add an entry to the table
98    pub fn add(&mut self, entry: T) -> Result<(), String> {
99        let name = entry.name().to_uppercase();
100        if self.entries.contains_key(&name) {
101            return Err(format!("Entry '{}' already exists in table", entry.name()));
102        }
103        self.entries.insert(name, entry);
104        Ok(())
105    }
106
107    /// Add or replace an entry in the table (parsed data wins over defaults)
108    pub fn add_or_replace(&mut self, entry: T) {
109        let name = entry.name().to_uppercase();
110        self.entries.insert(name, entry);
111    }
112
113    /// Get an entry by name (case-insensitive)
114    pub fn get(&self, name: &str) -> Option<&T> {
115        self.entries.get(&name.to_uppercase())
116    }
117
118    /// Get a mutable entry by name (case-insensitive)
119    pub fn get_mut(&mut self, name: &str) -> Option<&mut T> {
120        self.entries.get_mut(&name.to_uppercase())
121    }
122
123    /// Remove an entry by name (case-insensitive)
124    pub fn remove(&mut self, name: &str) -> Option<T> {
125        self.entries.shift_remove(&name.to_uppercase())
126    }
127
128    /// Check if an entry exists (case-insensitive)
129    pub fn contains(&self, name: &str) -> bool {
130        self.entries.contains_key(&name.to_uppercase())
131    }
132
133    /// Get the number of entries
134    pub fn len(&self) -> usize {
135        self.entries.len()
136    }
137
138    /// Check if the table is empty
139    pub fn is_empty(&self) -> bool {
140        self.entries.is_empty()
141    }
142
143    /// Iterate over all entries
144    pub fn iter(&self) -> impl Iterator<Item = &T> {
145        self.entries.values()
146    }
147
148    /// Iterate over all entries mutably
149    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
150        self.entries.values_mut()
151    }
152
153    /// Get all entry names
154    pub fn names(&self) -> impl Iterator<Item = &str> {
155        self.entries.values().map(|e| e.name())
156    }
157
158    /// Clear all entries
159    pub fn clear(&mut self) {
160        self.entries.clear();
161    }
162}
163
164impl<T: TableEntry> Default for Table<T> {
165    fn default() -> Self {
166        Self::new()
167    }
168}
169
170#[cfg(test)]
171mod tests {
172    use super::*;
173
174    // Mock table entry for testing
175    #[derive(Debug, Clone)]
176    struct MockEntry {
177        handle: Handle,
178        name: String,
179    }
180
181    impl TableEntry for MockEntry {
182        fn handle(&self) -> Handle {
183            self.handle
184        }
185
186        fn set_handle(&mut self, handle: Handle) {
187            self.handle = handle;
188        }
189
190        fn name(&self) -> &str {
191            &self.name
192        }
193
194        fn set_name(&mut self, name: String) {
195            self.name = name;
196        }
197    }
198
199    #[test]
200    fn test_table_add_and_get() {
201        let mut table = Table::new();
202        let entry = MockEntry {
203            handle: Handle::new(1),
204            name: "Test".to_string(),
205        };
206        
207        assert!(table.add(entry).is_ok());
208        assert!(table.contains("Test"));
209        assert!(table.contains("test")); // Case-insensitive
210        assert_eq!(table.len(), 1);
211    }
212
213    #[test]
214    fn test_table_duplicate_entry() {
215        let mut table = Table::new();
216        let entry1 = MockEntry {
217            handle: Handle::new(1),
218            name: "Test".to_string(),
219        };
220        let entry2 = MockEntry {
221            handle: Handle::new(2),
222            name: "test".to_string(), // Same name, different case
223        };
224        
225        assert!(table.add(entry1).is_ok());
226        assert!(table.add(entry2).is_err()); // Should fail
227    }
228
229    #[test]
230    fn test_table_remove() {
231        let mut table = Table::new();
232        let entry = MockEntry {
233            handle: Handle::new(1),
234            name: "Test".to_string(),
235        };
236        
237        table.add(entry).unwrap();
238        assert_eq!(table.len(), 1);
239        
240        let removed = table.remove("test");
241        assert!(removed.is_some());
242        assert_eq!(table.len(), 0);
243    }
244}
245
246