parse_zoneinfo/
structure.rs1use std::collections::{BTreeMap, BTreeSet};
34
35use crate::table::Table;
36
37pub trait Structure {
39 fn structure(&self) -> TableStructure;
41}
42
43impl Structure for Table {
44 fn structure(&self) -> TableStructure {
45 let mut mappings = BTreeMap::new();
46
47 for key in self.zonesets.keys().chain(self.links.keys()) {
48 let last_slash = match key.rfind('/') {
52 Some(pos) => pos,
53 None => continue,
54 };
55
56 let parent = &key[..last_slash];
58 {
59 let set = mappings.entry(parent).or_insert_with(BTreeSet::new);
60 set.insert(Child::TimeZone(&key[last_slash + 1..]));
61 }
62
63 if let Some(first_slash) = parent.find('/') {
68 let grandparent = &parent[..first_slash];
69 let set = mappings.entry(grandparent).or_insert_with(BTreeSet::new);
70 set.insert(Child::Submodule(&parent[first_slash + 1..]));
71 }
72 }
73
74 TableStructure { mappings }
75 }
76}
77
78#[derive(PartialEq, Debug)]
80pub struct TableStructure<'table> {
81 mappings: BTreeMap<&'table str, BTreeSet<Child<'table>>>,
82}
83
84impl<'table> IntoIterator for TableStructure<'table> {
85 type Item = TableStructureEntry<'table>;
86 type IntoIter = Iter<'table>;
87
88 fn into_iter(self) -> Self::IntoIter {
89 let mut keys: Vec<_> = self.mappings.keys().cloned().collect();
93 keys.sort_by(|a, b| b.cmp(a));
94
95 Iter {
96 structure: self,
97 keys,
98 }
99 }
100}
101
102#[derive(PartialEq, Debug)]
104pub struct Iter<'table> {
105 structure: TableStructure<'table>,
106 keys: Vec<&'table str>,
107}
108
109impl<'table> Iterator for Iter<'table> {
110 type Item = TableStructureEntry<'table>;
111
112 fn next(&mut self) -> Option<Self::Item> {
113 let key = self.keys.pop()?;
114
115 let values = self.structure.mappings[key].iter().cloned().collect();
117
118 Some(TableStructureEntry {
119 name: key,
120 children: values,
121 })
122 }
123}
124
125#[derive(PartialEq, Debug)]
127pub struct TableStructureEntry<'table> {
128 pub name: &'table str,
130
131 pub children: Vec<Child<'table>>,
133}
134
135#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)]
141pub enum Child<'table> {
142 Submodule(&'table str),
144
145 TimeZone(&'table str),
147}
148
149#[cfg(test)]
150#[allow(unused_results)]
151mod test {
152 use super::*;
153 use crate::table::Table;
154
155 #[test]
156 fn empty() {
157 let table = Table::default();
158 let mut structure = table.structure().into_iter();
159 assert_eq!(structure.next(), None);
160 }
161
162 #[test]
163 fn separate() {
164 let mut table = Table::default();
165 table.zonesets.insert("a".to_owned(), Vec::new());
166 table.zonesets.insert("b".to_owned(), Vec::new());
167 table.zonesets.insert("c".to_owned(), Vec::new());
168
169 let mut structure = table.structure().into_iter();
170 assert_eq!(structure.next(), None);
171 }
172
173 #[test]
174 fn child() {
175 let mut table = Table::default();
176 table.zonesets.insert("a/b".to_owned(), Vec::new());
177
178 let mut structure = table.structure().into_iter();
179 assert_eq!(
180 structure.next(),
181 Some(TableStructureEntry {
182 name: "a",
183 children: vec![Child::TimeZone("b")]
184 })
185 );
186 assert_eq!(structure.next(), None);
187 }
188
189 #[test]
190 fn hierarchy() {
191 let mut table = Table::default();
192 table.zonesets.insert("a/b/c".to_owned(), Vec::new());
193 table.zonesets.insert("a/b/d".to_owned(), Vec::new());
194 table.zonesets.insert("a/e".to_owned(), Vec::new());
195
196 let mut structure = table.structure().into_iter();
197 assert_eq!(
198 structure.next(),
199 Some(TableStructureEntry {
200 name: "a",
201 children: vec![Child::Submodule("b"), Child::TimeZone("e")]
202 })
203 );
204 assert_eq!(
205 structure.next(),
206 Some(TableStructureEntry {
207 name: "a/b",
208 children: vec![Child::TimeZone("c"), Child::TimeZone("d")]
209 })
210 );
211 assert_eq!(structure.next(), None);
212 }
213}