1use std::collections::HashSet;
7
8#[derive(Debug, Clone)]
13pub struct Group {
14 name: String,
16 enabled: bool,
18 show_child_details: bool,
20 child_structures: HashSet<String>,
22 child_groups: HashSet<String>,
24 parent_group: Option<String>,
26}
27
28impl Group {
29 pub fn new(name: impl Into<String>) -> Self {
31 Self {
32 name: name.into(),
33 enabled: true,
34 show_child_details: true,
35 child_structures: HashSet::new(),
36 child_groups: HashSet::new(),
37 parent_group: None,
38 }
39 }
40
41 #[must_use]
43 pub fn name(&self) -> &str {
44 &self.name
45 }
46
47 #[must_use]
49 pub fn is_enabled(&self) -> bool {
50 self.enabled
51 }
52
53 pub fn set_enabled(&mut self, enabled: bool) {
55 self.enabled = enabled;
56 }
57
58 #[must_use]
60 pub fn show_child_details(&self) -> bool {
61 self.show_child_details
62 }
63
64 pub fn set_show_child_details(&mut self, show: bool) {
66 self.show_child_details = show;
67 }
68
69 #[must_use]
71 pub fn parent_group(&self) -> Option<&str> {
72 self.parent_group.as_deref()
73 }
74
75 pub fn set_parent_group(&mut self, parent: Option<String>) {
77 self.parent_group = parent;
78 }
79
80 pub fn add_structure(&mut self, type_name: &str, name: &str) {
84 self.child_structures.insert(format!("{type_name}:{name}"));
85 }
86
87 pub fn remove_structure(&mut self, type_name: &str, name: &str) {
89 self.child_structures.remove(&format!("{type_name}:{name}"));
90 }
91
92 #[must_use]
94 pub fn contains_structure(&self, type_name: &str, name: &str) -> bool {
95 self.child_structures
96 .contains(&format!("{type_name}:{name}"))
97 }
98
99 pub fn add_child_group(&mut self, group_name: &str) {
101 self.child_groups.insert(group_name.to_string());
102 }
103
104 pub fn remove_child_group(&mut self, group_name: &str) {
106 self.child_groups.remove(group_name);
107 }
108
109 #[must_use]
111 pub fn contains_child_group(&self, group_name: &str) -> bool {
112 self.child_groups.contains(group_name)
113 }
114
115 pub fn child_structures(&self) -> impl Iterator<Item = (&str, &str)> {
117 self.child_structures.iter().filter_map(|s| {
118 let parts: Vec<&str> = s.splitn(2, ':').collect();
119 if parts.len() == 2 {
120 Some((parts[0], parts[1]))
121 } else {
122 None
123 }
124 })
125 }
126
127 pub fn child_groups(&self) -> impl Iterator<Item = &str> {
129 self.child_groups.iter().map(std::string::String::as_str)
130 }
131
132 #[must_use]
134 pub fn num_child_structures(&self) -> usize {
135 self.child_structures.len()
136 }
137
138 #[must_use]
140 pub fn num_child_groups(&self) -> usize {
141 self.child_groups.len()
142 }
143
144 #[must_use]
146 pub fn is_empty(&self) -> bool {
147 self.child_structures.is_empty() && self.child_groups.is_empty()
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn test_group_creation() {
157 let group = Group::new("test_group");
158 assert_eq!(group.name(), "test_group");
159 assert!(group.is_enabled());
160 assert!(group.is_empty());
161 }
162
163 #[test]
164 fn test_add_structure() {
165 let mut group = Group::new("test");
166 group.add_structure("PointCloud", "my_points");
167 assert!(group.contains_structure("PointCloud", "my_points"));
168 assert!(!group.is_empty());
169 assert_eq!(group.num_child_structures(), 1);
170 }
171
172 #[test]
173 fn test_remove_structure() {
174 let mut group = Group::new("test");
175 group.add_structure("PointCloud", "my_points");
176 group.remove_structure("PointCloud", "my_points");
177 assert!(!group.contains_structure("PointCloud", "my_points"));
178 assert!(group.is_empty());
179 }
180
181 #[test]
182 fn test_group_hierarchy() {
183 let mut parent = Group::new("parent");
184 parent.add_child_group("child");
185 assert!(parent.contains_child_group("child"));
186 assert_eq!(parent.num_child_groups(), 1);
187
188 let mut child = Group::new("child");
189 assert!(child.parent_group().is_none());
190 child.set_parent_group(Some("parent".to_string()));
191 assert_eq!(child.parent_group(), Some("parent"));
192 }
193}