use std::collections::{BTreeMap, BTreeSet};
use crate::table::Table;
pub trait Structure {
fn structure(&self) -> TableStructure;
}
impl Structure for Table {
fn structure(&self) -> TableStructure {
let mut mappings = BTreeMap::new();
for key in self.zonesets.keys().chain(self.links.keys()) {
let last_slash = match key.rfind('/') {
Some(pos) => pos,
None => continue,
};
let parent = &key[..last_slash];
{
let set = mappings.entry(parent).or_insert_with(BTreeSet::new);
set.insert(Child::TimeZone(&key[last_slash + 1..]));
}
if let Some(first_slash) = parent.find('/') {
let grandparent = &parent[..first_slash];
let set = mappings.entry(grandparent).or_insert_with(BTreeSet::new);
set.insert(Child::Submodule(&parent[first_slash + 1..]));
}
}
TableStructure { mappings }
}
}
#[derive(PartialEq, Debug)]
pub struct TableStructure<'table> {
mappings: BTreeMap<&'table str, BTreeSet<Child<'table>>>,
}
impl<'table> IntoIterator for TableStructure<'table> {
type Item = TableStructureEntry<'table>;
type IntoIter = Iter<'table>;
fn into_iter(self) -> Self::IntoIter {
let mut keys: Vec<_> = self.mappings.keys().cloned().collect();
keys.sort_by(|a, b| b.cmp(a));
Iter {
structure: self,
keys,
}
}
}
#[derive(PartialEq, Debug)]
pub struct Iter<'table> {
structure: TableStructure<'table>,
keys: Vec<&'table str>,
}
impl<'table> Iterator for Iter<'table> {
type Item = TableStructureEntry<'table>;
fn next(&mut self) -> Option<Self::Item> {
let key = self.keys.pop()?;
let values = self.structure.mappings[key].iter().cloned().collect();
Some(TableStructureEntry {
name: key,
children: values,
})
}
}
#[derive(PartialEq, Debug)]
pub struct TableStructureEntry<'table> {
pub name: &'table str,
pub children: Vec<Child<'table>>,
}
#[derive(PartialEq, Eq, PartialOrd, Ord, Debug, Copy, Clone)]
pub enum Child<'table> {
Submodule(&'table str),
TimeZone(&'table str),
}
#[cfg(test)]
#[allow(unused_results)]
mod test {
use super::*;
use crate::table::Table;
#[test]
fn empty() {
let table = Table::default();
let mut structure = table.structure().into_iter();
assert_eq!(structure.next(), None);
}
#[test]
fn separate() {
let mut table = Table::default();
table.zonesets.insert("a".to_owned(), Vec::new());
table.zonesets.insert("b".to_owned(), Vec::new());
table.zonesets.insert("c".to_owned(), Vec::new());
let mut structure = table.structure().into_iter();
assert_eq!(structure.next(), None);
}
#[test]
fn child() {
let mut table = Table::default();
table.zonesets.insert("a/b".to_owned(), Vec::new());
let mut structure = table.structure().into_iter();
assert_eq!(
structure.next(),
Some(TableStructureEntry {
name: "a",
children: vec![Child::TimeZone("b")]
})
);
assert_eq!(structure.next(), None);
}
#[test]
fn hierarchy() {
let mut table = Table::default();
table.zonesets.insert("a/b/c".to_owned(), Vec::new());
table.zonesets.insert("a/b/d".to_owned(), Vec::new());
table.zonesets.insert("a/e".to_owned(), Vec::new());
let mut structure = table.structure().into_iter();
assert_eq!(
structure.next(),
Some(TableStructureEntry {
name: "a",
children: vec![Child::Submodule("b"), Child::TimeZone("e")]
})
);
assert_eq!(
structure.next(),
Some(TableStructureEntry {
name: "a/b",
children: vec![Child::TimeZone("c"), Child::TimeZone("d")]
})
);
assert_eq!(structure.next(), None);
}
}