cobalt/cobalt_model/
template.rs1use std::fmt;
2use std::path;
3
4use super::files;
5use crate::error::Result;
6use crate::syntax_highlight;
7use liquid;
8use log::warn;
9use log::{debug, trace};
10use serde::Serialize;
11
12#[derive(Debug, Clone, Serialize)]
13#[serde(deny_unknown_fields)]
14pub struct LiquidBuilder {
15 pub includes_path: path::PathBuf,
16 pub theme: Option<liquid::model::KString>,
17 #[serde(skip)]
18 pub syntax: std::sync::Arc<crate::SyntaxHighlight>,
19}
20
21impl LiquidBuilder {
22 pub fn build(self) -> Result<Liquid> {
23 let highlight = syntax_highlight::CodeBlockParser::new(self.syntax, self.theme)?;
24 let highlight: Box<dyn liquid_core::ParseBlock> = Box::new(highlight);
25 let parser = liquid::ParserBuilder::with_stdlib()
26 .filter(liquid_lib::extra::DateInTz)
27 .filter(liquid_lib::shopify::Pluralize)
28 .filter(liquid_lib::jekyll::Slugify)
30 .filter(liquid_lib::jekyll::Pop)
31 .filter(liquid_lib::jekyll::Push)
32 .filter(liquid_lib::jekyll::Shift)
33 .filter(liquid_lib::jekyll::Unshift)
34 .filter(liquid_lib::jekyll::ArrayToSentenceString)
35 .partials(load_partials_from_path(self.includes_path)?)
36 .block(highlight)
37 .build()?;
38 Ok(Liquid { parser })
39 }
40}
41
42type Partials = liquid::partials::EagerCompiler<liquid::partials::InMemorySource>;
43
44fn load_partials_from_path(root: path::PathBuf) -> Result<Partials> {
45 let mut source = Partials::empty();
46
47 debug!("Loading snippets from `{}`", root.display());
48 let template_files = files::FilesBuilder::new(root)?
49 .ignore_hidden(false)?
50 .build()?;
51 for file_path in template_files.files() {
52 let rel_path = file_path
53 .strip_prefix(template_files.root())
54 .expect("file was found under the root")
55 .to_str()
56 .expect("only UTF-8 characters supported in paths")
57 .to_owned();
58 trace!("Loading snippet `{rel_path}`");
59 match files::read_file(file_path) {
60 Ok(content) => {
61 source.add(rel_path, content);
62 }
63 Err(err) => {
64 warn!("Ignoring snippet {rel_path}: {err}");
65 }
66 }
67 }
68 Ok(source)
69}
70
71pub struct Liquid {
72 parser: liquid::Parser,
73}
74
75impl Liquid {
76 pub fn parse(&self, template: &str) -> Result<liquid::Template> {
77 let template = self.parser.parse(template)?;
78 Ok(template)
79 }
80}
81
82impl fmt::Debug for Liquid {
83 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
84 write!(f, "Liquid{{}}")
85 }
86}