css_knife/
config.rs

1use serde::Deserialize;
2use std::{error::Error, fs::metadata};
3
4#[derive(Debug, Deserialize)]
5pub struct Config {
6    pub html_dir: Vec<String>,
7    pub css_dir: Vec<String>,
8    pub output_dir: String,
9    pub assets_dir: Option<Vec<String>>,
10    pub js_map: Option<Vec<String>>,
11    pub ignored_files: Option<Vec<String>>,
12    pub macro_classes: Option<Vec<String>>,
13}
14
15pub fn read_config<'a>() -> Result<Config, ConfigError<'a>> {
16    match std::fs::read_to_string("css-knife.toml") {
17        Ok(s) => match toml::from_str::<Config>(&s) {
18            Ok(config) => Ok(config),
19            Err(e) => Err(ConfigError::ConfigNotFound(e.to_string())),
20        },
21        Err(e) => Err(ConfigError::ConfigNotFound(e.to_string())),
22    }
23}
24
25impl Config {
26    pub fn validate(&self) -> Result<(), ConfigError> {
27        self.check_dirs(
28            &self.html_dir,
29            ConfigError::EmptyDir(ConfigDir::HTMLDir),
30        )?;
31        self.check_dirs(
32            &self.css_dir,
33            ConfigError::EmptyDir(ConfigDir::CSSDir),
34        )?;
35        let output = metadata(&self.output_dir);
36        match output {
37            Ok(data) => {
38                if !data.is_dir() {
39                    return Err(ConfigError::NoOutputDir);
40                }
41            }
42            Err(_) => {
43                return Err(ConfigError::NoOutputDir);
44            }
45        }
46        if let Some(assets) = &self.assets_dir {
47            self.check_dirs(
48                assets,
49                ConfigError::EmptyDir(ConfigDir::AssetsDir),
50            )?;
51        }
52        if let Some(js) = &self.js_map {
53            self.check_dirs(js, ConfigError::EmptyDir(ConfigDir::JSDir))?;
54        }
55        if let Some(macro_classes) = &self.macro_classes {
56            self.check_dirs(
57                macro_classes,
58                ConfigError::EmptyDir(ConfigDir::MacroClasses),
59            )?;
60        }
61        Ok(())
62    }
63
64    fn check_dirs<'a>(
65        &self,
66        dirs: &'a Vec<String>,
67        err: ConfigError<'a>,
68    ) -> Result<(), ConfigError<'a>> {
69        let mut empty = true;
70        for dir in dirs {
71            empty = false;
72            if let Ok(data) = metadata(dir) {
73                if !data.is_dir() {
74                    return Err(ConfigError::NotDir(dir));
75                }
76            }
77        }
78        if empty {
79            Err(err)
80        } else {
81            Ok(())
82        }
83    }
84}
85
86#[derive(Debug)]
87pub enum ConfigDir {
88    HTMLDir,
89    CSSDir,
90    JSDir,
91    AssetsDir,
92    IgnoredCssDir,
93    MacroClasses,
94}
95
96impl std::fmt::Display for ConfigDir {
97    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
98        match self {
99            ConfigDir::HTMLDir => write!(f, "HTML"),
100            ConfigDir::CSSDir => write!(f, "CSS"),
101            ConfigDir::JSDir => write!(f, "JS"),
102            ConfigDir::AssetsDir => write!(f, "Asset"),
103            ConfigDir::IgnoredCssDir => write!(f, "ignored css files"),
104            ConfigDir::MacroClasses => write!(f, "macro files"),
105        }
106    }
107}
108
109#[derive(Debug)]
110pub enum ConfigError<'a> {
111    NoOutputDir,
112    EmptyDir(ConfigDir),
113    NotDir(&'a String),
114    ConfigNotFound(String),
115}
116
117impl<'a> std::fmt::Display for ConfigError<'a> {
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        match &self {
120            ConfigError::NoOutputDir => {
121                write!(f, "Output directory not found.")
122            }
123            ConfigError::EmptyDir(dir) => {
124                write!(f, "Array of directories for {} is empty.", dir)
125            }
126            ConfigError::NotDir(item) => {
127                write!(f, "Specified item({}) is not directory.", item)
128            }
129            ConfigError::ConfigNotFound(err) => {
130                write!(f, "Config not found: {}", err)
131            }
132        }
133    }
134}
135
136impl<'a> Error for ConfigError<'a> {}