cobalt/cobalt_model/
config.rs

1use std::fmt;
2use std::path;
3
4use log::debug;
5use serde::Serialize;
6use serde_yaml;
7
8use crate::error::Result;
9
10use super::assets;
11use super::collection;
12use super::mark;
13use super::site;
14use super::template;
15use crate::SyntaxHighlight;
16
17#[derive(Debug, Clone, Serialize)]
18#[serde(deny_unknown_fields, default)]
19pub struct Config {
20    pub source: path::PathBuf,
21    pub destination: path::PathBuf,
22    pub ignore: Vec<liquid::model::KString>,
23    pub page_extensions: Vec<liquid::model::KString>,
24    pub include_drafts: bool,
25    pub pages: collection::Collection,
26    pub posts: collection::Collection,
27    pub site: site::Site,
28    pub layouts_path: path::PathBuf,
29    pub liquid: template::LiquidBuilder,
30    pub markdown: mark::MarkdownBuilder,
31    #[serde(skip)]
32    pub syntax: std::sync::Arc<SyntaxHighlight>,
33    pub assets: assets::AssetsBuilder,
34    pub minify: cobalt_config::Minify,
35}
36
37impl Config {
38    pub fn from_config(source: cobalt_config::Config) -> Result<Self> {
39        let cobalt_config::Config {
40            root,
41            source,
42            destination,
43            abs_dest,
44            include_drafts,
45            default,
46            pages,
47            posts,
48            site,
49            template_extensions,
50            ignore: custom_ignore,
51            syntax_highlight,
52            layouts_dir,
53            includes_dir,
54            assets,
55            minify,
56        } = source;
57
58        if include_drafts {
59            debug!("Draft mode enabled");
60        }
61
62        if template_extensions.is_empty() {
63            anyhow::bail!("`template_extensions` should not be empty.");
64        }
65
66        let source = source.to_path(&root);
67        let destination = abs_dest.unwrap_or_else(|| destination.to_path(root));
68
69        let pages = collection::Collection::from_page_config(pages, &site, &default)?;
70
71        let posts =
72            collection::Collection::from_post_config(posts, &site, include_drafts, &default)?;
73
74        let site = site::Site::from_config(site);
75
76        let mut ignore: Vec<liquid::model::KString> = vec![".*".into(), "_*".into()];
77        if let Ok(rel_dest) = path::Path::new(&destination).strip_prefix(&source) {
78            let rel_dest = rel_dest.to_str().expect("started as a utf-8 string");
79            if !rel_dest.is_empty() {
80                ignore.push(format!("/{}", rel_dest.to_owned()).into());
81            }
82        }
83        ignore.push(format!("/{includes_dir}").into());
84        ignore.push(format!("/{layouts_dir}").into());
85        ignore.push("/_defaults".into());
86        ignore.push(format!("/{}", assets.sass.import_dir).into());
87        assert_eq!(pages.dir, "");
88        assert_eq!(pages.drafts_dir, None);
89        ignore.push(format!("!/{}", posts.dir).into());
90        if let Some(dir) = posts.drafts_dir.as_deref() {
91            ignore.push(format!("!/{dir}").into());
92        }
93        ignore.extend(custom_ignore);
94
95        let assets = assets::AssetsBuilder::from_config(assets, &source);
96
97        let includes_path = source.join(includes_dir);
98        let layouts_path = source.join(layouts_dir);
99
100        let mut highlight = SyntaxHighlight::new();
101        let syntaxes_path = source.join("_syntaxes");
102        if syntaxes_path.exists() {
103            highlight.load_custom_syntaxes(&syntaxes_path);
104        }
105
106        let syntax = std::sync::Arc::new(highlight);
107
108        let liquid = template::LiquidBuilder {
109            includes_path,
110            syntax: syntax.clone(),
111            theme: syntax_highlight
112                .enabled
113                .then(|| syntax_highlight.theme.clone()),
114        };
115        let markdown = mark::MarkdownBuilder {
116            syntax: syntax.clone(),
117            theme: syntax_highlight
118                .enabled
119                .then(|| syntax_highlight.theme.clone()),
120        };
121
122        let config = Config {
123            source,
124            destination,
125            ignore,
126            page_extensions: template_extensions,
127            include_drafts,
128            pages,
129            posts,
130            site,
131            layouts_path,
132            liquid,
133            markdown,
134            syntax,
135            assets,
136            minify,
137        };
138
139        Ok(config)
140    }
141}
142
143impl Default for Config {
144    fn default() -> Config {
145        Config::from_config(cobalt_config::Config::default())
146            .expect("default config should not fail")
147    }
148}
149
150impl fmt::Display for Config {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        let mut converted = serde_yaml::to_string(self).map_err(|_| fmt::Error)?;
153        converted.drain(..4);
154        write!(f, "{converted}")
155    }
156}
157
158#[test]
159fn test_build_default() {
160    let config = cobalt_config::Config::default();
161    Config::from_config(config).unwrap();
162}
163
164#[test]
165fn test_build_dest() {
166    let config = cobalt_config::Config::from_file("tests/fixtures/config/_cobalt.yml").unwrap();
167    let result = Config::from_config(config).unwrap();
168    assert_eq!(
169        result.source,
170        path::Path::new("tests/fixtures/config").to_path_buf()
171    );
172    assert_eq!(
173        result.destination,
174        path::Path::new("tests/fixtures/config/dest").to_path_buf()
175    );
176}
177
178#[test]
179fn test_build_abs_dest() {
180    let mut config = cobalt_config::Config::from_file("tests/fixtures/config/_cobalt.yml").unwrap();
181    config.abs_dest = Some(path::PathBuf::from("hello/world"));
182    let result = Config::from_config(config).unwrap();
183    assert_eq!(
184        result.source,
185        path::Path::new("tests/fixtures/config").to_path_buf()
186    );
187    assert_eq!(
188        result.destination,
189        path::Path::new("hello/world").to_path_buf()
190    );
191}