texted 1.2.1

A markdown blog platform that lets you in control of your data
Documentation
use std::io::ErrorKind;
use std::path::PathBuf;
use std::{env, fs, io};

use serde::Deserialize;

use crate::util::toml_date::TomlDate;

#[derive(Deserialize)]
pub struct Paths {
    pub template_dir: PathBuf,
    pub public_dir: PathBuf,
    pub posts_dir: PathBuf,
    pub pages_dir: PathBuf,
}

#[derive(Deserialize)]
pub struct Personal {
    pub activity_start_year: i32,
    pub blog_start_date: TomlDate,
}


#[derive(Deserialize)]
pub struct Defaults {
    pub index_base_name: Option<String>,
    pub summary_line_count: Option<i32>,
    pub summary_line_tag: Option<String>,
    pub page_size: u32,
    pub rendering_cache_enabled: bool,
}

#[derive(Deserialize)]
pub struct Server {
    pub address: String,
    pub port: u16,
}

#[derive(Deserialize)]
pub struct Log {
    pub level: LogLevel,
    pub log_to_console: bool,
    pub location: Option<PathBuf>,
}

#[derive(Deserialize, Copy, Clone)]
pub enum LogLevel {
    Critical = 0,
    Error,
    Warn,
    Info,
    Debug,
    Trace,
}

#[derive(Deserialize)]
pub struct Metrics {
    pub location: Option<PathBuf>,
    pub time_slot_secs: Option<i64>,
}

#[derive(Deserialize)]
pub struct RssFeed {
    pub title: String,
    pub site_url: String,
    pub description: String,
    pub page_size: u32,
}

#[derive(Deserialize)]
pub struct Config {
    pub personal: Personal,
    pub paths: Paths,
    pub defaults: Defaults,
    pub server: Server,
    pub log: Option<Log>,
    pub metrics: Option<Metrics>,
    pub rss_feed: Option<RssFeed>,
}

fn parse_path(path: PathBuf) -> PathBuf {
    if path.starts_with("${exe_dir}") {
        let cur_exe = env::current_exe().unwrap();
        let exe_dir = cur_exe.parent().unwrap().to_str().unwrap();
        let str_path = path.to_str().unwrap();
        PathBuf::from(str_path.replace("${exe_dir}", exe_dir))
    } else {
        path
    }
}

pub fn read_config(cfg_path: &PathBuf) -> io::Result<Config> {
    let cfg_content = match fs::read_to_string(cfg_path) {
        Ok(content) => content,
        Err(e) => return Err(io::Error::new(e.kind(), format!("Error opening configuration file {}: {}", cfg_path.to_str().unwrap(), e))),
    };

    let mut cfg: Config = match toml::from_str::<Config>(cfg_content.as_str()) {
        Ok(cfg) => cfg,
        Err(e) => return Err(io::Error::new(
            ErrorKind::InvalidData, format!("Error parsing configuration file: {}", e))),
    };

    cfg.paths = Paths {
        template_dir: parse_path(cfg.paths.template_dir),
        public_dir: parse_path(cfg.paths.public_dir),
        posts_dir: parse_path(cfg.paths.posts_dir),
        pages_dir: parse_path(cfg.paths.pages_dir),
    };

    Ok(cfg)
}