wayver's git archive


an obsidian renderer
git clone https://git.wayver.dev/sable

sable-core/src/config.rs@2b84405277e54ab809e328cf0237374d4b4dbd0c

raw
Date Commit Message Author Files + -
2026-02-23 01:55 initial mvp wayverd 139 17808 0
...

1use std::{collections::HashMap, path::PathBuf, sync::LazyLock};
2
3static CWD: LazyLock<PathBuf> = LazyLock::new(|| {
4    std::env::current_dir()
5        .expect("failed to get current working directory")
6        .canonicalize()
7        .expect("failed to canonicalize current working directory")
8});
9
10#[derive(Debug, miette::Diagnostic, thiserror::Error)]
11pub enum ConfigError {
12    #[error("failed to read config file")]
13    ReadFile(#[source] std::io::Error),
14    #[error("failed to deserialize config file")]
15    Deserialize(#[source] serde_toml::de::Error),
16    #[error("failed to create missing directory")]
17    CreateDir(#[source] std::io::Error),
18    #[error("failed to canonicalize config's file paths ({1})")]
19    Canonicalize(#[source] std::io::Error, PathBuf),
20}
21
22#[derive(Debug, Clone, serde::Deserialize)]
23#[serde(default)]
24pub struct Config {
25    pub build: PathBuf,
26    pub r#static: PathBuf,
27    pub templates: PathBuf,
28    pub vault: PathBuf,
29
30    pub port: u16,
31
32    pub default_template: Option<String>,
33
34    pub assets: Vec<AssetConfig>,
35
36    pub modules: ModulesConfig,
37
38    pub pages: Vec<PageConfig>,
39
40    pub data: Option<serde_json::Value>,
41}
42
43impl Config {
44    pub fn load() -> Result<Self, ConfigError> {
45        let path = CWD.join("sable.toml");
46
47        let config = if path.exists() {
48            let config = std::fs::read_to_string(path).map_err(ConfigError::ReadFile)?;
49
50            let mut config: Self =
51                serde_toml::from_str(&config).map_err(ConfigError::Deserialize)?;
52
53            config.canonicalize()?;
54
55            config
56        } else {
57            Self::default()
58        };
59
60        Ok(config)
61    }
62
63    fn canonicalize(&mut self) -> Result<(), ConfigError> {
64        std::fs::create_dir_all(&self.build).map_err(ConfigError::CreateDir)?;
65
66        self.build = self
67            .build
68            .canonicalize()
69            .map_err(|err| ConfigError::Canonicalize(err, self.build.clone()))?;
70        self.r#static = self
71            .r#static
72            .canonicalize()
73            .map_err(|err| ConfigError::Canonicalize(err, self.r#static.clone()))?;
74        self.templates = self
75            .templates
76            .canonicalize()
77            .map_err(|err| ConfigError::Canonicalize(err, self.templates.clone()))?;
78        self.vault = self
79            .vault
80            .canonicalize()
81            .map_err(|err| ConfigError::Canonicalize(err, self.vault.clone()))?;
82
83        Ok(())
84    }
85}
86
87impl Default for Config {
88    fn default() -> Self {
89        Self {
90            build: CWD.join("dist"),
91            r#static: CWD.join("static"),
92            templates: CWD.join("templates"),
93            vault: CWD.join("content"),
94
95            port: 3000,
96
97            default_template: None,
98
99            assets: Vec::new(),
100
101            modules: ModulesConfig::default(),
102
103            pages: Vec::new(),
104
105            data: None,
106        }
107    }
108}
109
110#[derive(Debug, Clone, serde::Deserialize)]
111pub struct AssetConfig {
112    pub output: PathBuf,
113    pub command: String,
114    #[serde(default)]
115    pub env: Option<HashMap<String, String>>,
116}
117
118#[derive(Debug, Clone, Default, serde::Deserialize)]
119#[serde(default)]
120pub struct ModulesConfig {
121    pub rss: RssConfig,
122}
123
124#[derive(Debug, Clone, Default, serde::Deserialize)]
125#[serde(default)]
126pub struct RssConfig {
127    pub enabled: bool,
128    pub template: String,
129    pub path: String,
130}
131
132#[derive(Debug, Clone, Default, serde::Deserialize)]
133#[serde(default)]
134pub struct PageConfig {
135    pub template: String,
136    pub path: String,
137    pub metadata: Option<serde_json::Value>,
138}
139