crabmail

Static HTML email archive viewer in Rust
git clone git://git.alexwennerberg.com/crabmail
Log | Files | Refs | README | LICENSE

config.rs (4319B) - raw


      1 // This file is licensed under the terms of 0BSD:
      2 //
      3 // Permission to use, copy, modify, and/or distribute this software for any purpose with or without
      4 // fee is hereby granted.
      5 //
      6 // THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS
      7 // SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
      8 // AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      9 // WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
     10 // NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     11 // OF THIS SOFTWARE.
     12 
     13 use once_cell::sync::OnceCell;
     14 use std::fs::File;
     15 use std::io::{self, BufRead};
     16 use std::path::{Path, PathBuf};
     17 
     18 // Ini-like configuration, with sections.
     19 // Global config first, then config for each subsection
     20 // key=value
     21 //
     22 // [section]
     23 // key2=value2
     24 #[derive(Default, Debug)]
     25 pub struct Config {
     26     pub email_fmt: String,
     27     pub title_fmt: String,
     28     pub base_url: String,
     29     pub description: String,
     30     pub include_gemini: bool,
     31     pub include_html: bool,
     32     pub out_dir: PathBuf, // TODO rem
     33     pub now: String,      // TODO rem
     34     pub subsections: Vec<Subsection>,
     35 }
     36 
     37 impl Config {
     38     // TODO defaults here
     39     pub fn match_kv(&mut self, key: &str, value: &str) {
     40         match key {
     41             "email_fmt" => self.email_fmt = value.to_string(),
     42             "title_fmt" => self.title_fmt = value.to_string(),
     43             "base_url" => self.base_url = value.to_string(),
     44             "description" => self.description = value.to_string(),
     45             _ => {}
     46         }
     47     }
     48 }
     49 
     50 #[derive(Default, Clone, Debug)]
     51 pub struct Subsection {
     52     pub name: String,  // something
     53     pub title: String, // something mail archive
     54     pub email: String,
     55     pub description: String,
     56 }
     57 
     58 impl Subsection {
     59     // TODO defaults here
     60     fn match_kv(&mut self, key: &str, value: &str) {
     61         match key {
     62             "title" => self.title = value.to_string(),
     63             "email" => self.email = value.to_string(),
     64             "description" => self.description = value.to_string(),
     65             _ => {}
     66         }
     67     }
     68 }
     69 
     70 pub static INSTANCE: OnceCell<Config> = OnceCell::new();
     71 
     72 impl Config {
     73     pub fn global() -> &'static Config {
     74         INSTANCE.get().expect("Config is not initialized")
     75     }
     76 
     77     pub fn default_subsection(&self, name: &str) -> Subsection {
     78         Subsection {
     79             name: name.to_owned(),
     80             title: self.title_fmt.replace("%s", name),
     81             email: self.email_fmt.replace("%s", name),
     82             description: String::new(),
     83         }
     84     }
     85 
     86     pub fn get_subsection(&self, name: &str) -> Option<Subsection> {
     87         // ugly
     88         for sub in self.subsections.clone() {
     89             if sub.name == name {
     90                 return Some(sub);
     91             }
     92         }
     93         return None;
     94     }
     95 
     96     pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Config, std::io::Error> {
     97         let file = File::open(path)?;
     98         // let sub_sections = vec![];
     99         let mut conf = Config::default();
    100         let mut current_section = None;
    101 
    102         for l in io::BufReader::new(file).lines() {
    103             let line = l?;
    104             if line.starts_with("[") && line.ends_with("]") {
    105                 let name = &line[1..line.len() - 1];
    106                 // Defaults from global config
    107                 if current_section.is_some() {
    108                     conf.subsections.push(current_section.unwrap());
    109                 }
    110                 current_section = Some(conf.default_subsection(name))
    111             }
    112             if line.len() == 0 {
    113                 continue;
    114             }
    115             if let Some(i) = line.find('=') {
    116                 let key = &line[..i];
    117                 let value = &line[i + 1..];
    118                 if let Some(ref mut s) = current_section {
    119                     s.match_kv(key, value);
    120                 } else {
    121                     conf.match_kv(key, value);
    122                 }
    123             } else {
    124                 // panic!("Invalid config")
    125             }
    126         }
    127         if current_section.is_some() {
    128             conf.subsections.push(current_section.unwrap());
    129         }
    130         conf.now = crate::time::current_time_rfc3339(); // TODO remove
    131         Ok(conf)
    132     }
    133 }