use crate::schema::{Config, List, Person, Team}; use failure::{Error, ResultExt}; use serde::Deserialize; use std::collections::HashMap; use std::ffi::OsStr; use std::path::Path; #[derive(Debug)] pub(crate) struct Data { people: HashMap, teams: HashMap, config: Config, } impl Data { pub(crate) fn load() -> Result { let mut data = Data { people: HashMap::new(), teams: HashMap::new(), config: load_file(&Path::new("config.toml"))?, }; data.load_dir("people", |this, person: Person| { person.validate()?; this.people.insert(person.github().to_string(), person); Ok(()) })?; data.load_dir("teams", |this, team: Team| { this.teams.insert(team.name().to_string(), team); Ok(()) })?; Ok(data) } fn load_dir(&mut self, dir: &str, f: F) -> Result<(), Error> where T: for<'de> Deserialize<'de>, F: Fn(&mut Self, T) -> Result<(), Error>, { for entry in std::fs::read_dir(dir)? { let path = entry?.path(); if path.is_file() && path.extension() == Some(OsStr::new("toml")) { f(self, load_file(&path)?)?; } } Ok(()) } pub(crate) fn config(&self) -> &Config { &self.config } pub(crate) fn lists(&self) -> Result, Error> { let mut lists = HashMap::new(); for team in self.teams.values() { for list in team.lists(self)? { lists.insert(list.address().to_string(), list); } } Ok(lists) } pub(crate) fn list(&self, name: &str) -> Result, Error> { let mut lists = self.lists()?; Ok(lists.remove(name)) } pub(crate) fn team(&self, name: &str) -> Option<&Team> { self.teams.get(name) } pub(crate) fn teams(&self) -> impl Iterator { self.teams.values() } pub(crate) fn person(&self, name: &str) -> Option<&Person> { self.people.get(name) } pub(crate) fn people(&self) -> impl Iterator { self.people.values() } } fn load_file Deserialize<'de>>(path: &Path) -> Result { let content = std::fs::read(&path).with_context(|_| format!("failed to read {}", path.display()))?; let parsed = toml::from_slice(&content) .with_context(|_| format!("failed to parse {}", path.display()))?; Ok(parsed) }