summaryrefslogtreecommitdiffstats
path: root/src/data.rs
blob: 42fc700537c0ffbb0dcd7f91c58cd9aaeef79ff8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
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<String, Person>,
    teams: HashMap<String, Team>,
    config: Config,
}

impl Data {
    pub(crate) fn load() -> Result<Self, Error> {
        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<T, F>(&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<HashMap<String, List>, 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<Option<List>, 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<Item = &Team> {
        self.teams.values()
    }

    pub(crate) fn person(&self, name: &str) -> Option<&Person> {
        self.people.get(name)
    }

    pub(crate) fn people(&self) -> impl Iterator<Item = &Person> {
        self.people.values()
    }
}

fn load_file<T: for<'de> Deserialize<'de>>(path: &Path) -> Result<T, Error> {
    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)
}