use std::collections::{BTreeMap, BTreeSet};
use 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: 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: 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> {
loop {
let key = match self.keys.pop() {
Some(k) => k,
None => return None,
};
let values = self.structure.mappings[key].iter().cloned().collect();
return 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 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".to_owned(), 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".to_owned(), children: vec![ Child::Submodule("b"), Child::TimeZone("e") ] }));
assert_eq!(structure.next(), Some(TableStructureEntry { name: &"a/b".to_owned(), children: vec![ Child::TimeZone("c"), Child::TimeZone("d") ] }));
assert_eq!(structure.next(), None);
}
}