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
 96
 97
 98
 99
100
101
102
103
#[macro_use]
extern crate serde_derive;

use std::{
    env, fs,
    path::{Path, PathBuf},
};

#[derive(Debug)]
pub struct Config<'a> {
    pub dirs: Vec<PathBuf>,
    pub debug: Print<'a>,
}

impl<'a> Config<'a> {
    pub fn new(s: &str) -> Config {
        let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
        let default_dirs = vec![root.join("templates")];

        let raw: RawConfig =
            toml::from_str(&s).expect(&format!("invalid TOML in {}", CONFIG_FILE_NAME));

        let dirs = match raw.main {
            Some(Main { dir }) => dir.map_or(default_dirs, |v| {
                v.iter().map(|dir| root.join(dir)).collect()
            }),
            None => default_dirs,
        };

        Config {
            dirs,
            debug: raw.debug.unwrap_or_default(),
        }
    }

    pub fn find_template(&self, path: &str, start_at: Option<&Path>) -> PathBuf {
        if let Some(root) = start_at {
            let relative = root.with_file_name(path);
            if relative.exists() {
                return relative;
            }
        }

        for dir in &self.dirs {
            let rooted = dir.join(path);
            if rooted.exists() {
                return rooted;
            }
        }

        panic!(
            "template {:?} not found in directories {:?}",
            path, self.dirs
        )
    }
}

#[derive(Deserialize)]
struct RawConfig<'d> {
    #[serde(borrow)]
    main: Option<Main<'d>>,
    #[serde(borrow)]
    debug: Option<Print<'d>>,
}

#[derive(Debug, Deserialize)]
pub struct Print<'a> {
    #[serde(borrow)]
    pub theme: Option<&'a str>,
    pub number_line: Option<bool>,
}

impl<'a> Default for Print<'a> {
    fn default() -> Self {
        Self {
            theme: None,
            number_line: None,
        }
    }
}

#[derive(Deserialize)]
struct Main<'a> {
    #[serde(borrow)]
    dir: Option<Vec<&'a str>>,
}

pub fn read_config_file() -> String {
    let filename = config_file_path();
    if filename.exists() {
        fs::read_to_string(&filename)
            .expect(&format!("unable to read {}", filename.to_str().unwrap()))
    } else {
        "".to_string()
    }
}

#[inline]
pub fn config_file_path() -> PathBuf {
    PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()).join(CONFIG_FILE_NAME)
}

static CONFIG_FILE_NAME: &str = "yarte.toml";