From 4bd302c68817fc78ff31594b1175a3b67cece171 Mon Sep 17 00:00:00 2001 From: bott Date: Fri, 5 Oct 2018 05:51:22 +0200 Subject: Add changing delimiters support --- askama_shared/src/lib.rs | 223 ++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 201 insertions(+), 22 deletions(-) (limited to 'askama_shared') diff --git a/askama_shared/src/lib.rs b/askama_shared/src/lib.rs index 5df6998..46efea0 100644 --- a/askama_shared/src/lib.rs +++ b/askama_shared/src/lib.rs @@ -17,29 +17,74 @@ mod escaping; pub use error::{Error, Result}; pub use escaping::MarkupDisplay; + +use std::collections::BTreeMap; + pub mod filters; -pub struct Config { +#[derive(Debug)] +pub struct Config<'a> { pub dirs: Vec, + pub syntaxes: BTreeMap>, + pub default_syntax: &'a str, } -impl Config { - pub fn new() -> Config { - let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); - let filename = root.join(CONFIG_FILE_NAME); - if filename.exists() { - Self::from_str( - &fs::read_to_string(&filename) - .expect(&format!("unable to read {}", filename.to_str().unwrap())), - ) - } else { - Self::from_str("") +#[derive(Debug)] +pub struct Syntax<'a> { + pub block_start: &'a str, + pub block_end: &'a str, + pub expr_start: &'a str, + pub expr_end: &'a str, + pub comment_start: &'a str, + pub comment_end: &'a str, +} + +impl<'a> Default for Syntax<'a> { + fn default() -> Self { + Self { + block_start: "{%", + block_end: "%}", + expr_start: "{{", + expr_end: "}}", + comment_start: "{#", + comment_end: "#}", } } +} - pub fn from_str(s: &str) -> Self { +impl<'a> From> for Syntax<'a> { + fn from(raw: RawSyntax<'a>) -> Self { + let syntax = Self::default(); + Self { + block_start: raw.block_start.unwrap_or(syntax.block_start), + block_end: raw.block_end.unwrap_or(syntax.block_end), + expr_start: raw.expr_start.unwrap_or(syntax.expr_start), + expr_end: raw.expr_end.unwrap_or(syntax.expr_end), + comment_start: raw.comment_start.unwrap_or(syntax.comment_start), + comment_end: raw.comment_end.unwrap_or(syntax.comment_end), + } + } +} + +pub fn read_config_file() -> String { + let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); + let filename = root.join(CONFIG_FILE_NAME); + if filename.exists() { + fs::read_to_string(&filename) + .expect(&format!("unable to read {}", filename.to_str().unwrap())) + } else { + "".to_string() + } +} + +impl<'a> Config<'a> { + pub fn new(s: &str) -> Config { let root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); let default = vec![root.join("templates")]; + + let mut syntaxes: BTreeMap = BTreeMap::new(); + syntaxes.insert(DEFAULT_SYNTAX_NAME.to_string(), Syntax::default()); + let raw: RawConfig = toml::from_str(&s).expect(&format!("invalid TOML in {}", CONFIG_FILE_NAME)); @@ -50,7 +95,31 @@ impl Config { Some(General { dirs: None }) | None => default, }; - Self { dirs } + if let Some(raw_syntaxes) = raw.syntax { + for raw_s in raw_syntaxes { + let name = raw_s.name; + + if let Some(_) = syntaxes.insert(name.to_string(), Syntax::from(raw_s)) { + panic!("named syntax \"{}\" already exist", name) + } + } + } + + let default_syntax = if let Some(default_syntax) = raw.default_syntax { + if syntaxes.contains_key(default_syntax) { + default_syntax + } else { + panic!("default syntax {} not exist", default_syntax) + } + } else { + DEFAULT_SYNTAX_NAME + }; + + Config { + dirs, + syntaxes, + default_syntax, + } } pub fn find_template(&self, path: &str, start_at: Option<&Path>) -> PathBuf { @@ -76,8 +145,10 @@ impl Config { } #[derive(Deserialize)] -struct RawConfig { +struct RawConfig<'d> { general: Option, + syntax: Option>>, + default_syntax: Option<&'d str>, } #[derive(Deserialize)] @@ -85,11 +156,23 @@ struct General { dirs: Option>, } +#[derive(Deserialize)] +struct RawSyntax<'a> { + name: &'a str, + block_start: Option<&'a str>, + block_end: Option<&'a str>, + expr_start: Option<&'a str>, + expr_end: Option<&'a str>, + comment_start: Option<&'a str>, + comment_end: Option<&'a str>, +} + static CONFIG_FILE_NAME: &str = "askama.toml"; +static DEFAULT_SYNTAX_NAME: &str = "default"; #[cfg(test)] mod tests { - use super::Config; + use super::*; use std::env; use std::path::{Path, PathBuf}; @@ -97,7 +180,7 @@ mod tests { fn test_default_config() { let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); root.push("templates"); - let config = Config::from_str(""); + let config = Config::new(""); assert_eq!(config.dirs, vec![root]); } @@ -105,7 +188,7 @@ mod tests { fn test_config_dirs() { let mut root = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap()); root.push("tpl"); - let config = Config::from_str("[general]\ndirs = [\"tpl\"]"); + let config = Config::new("[general]\ndirs = [\"tpl\"]"); assert_eq!(config.dirs, vec![root]); } @@ -119,7 +202,7 @@ mod tests { #[test] fn find_absolute() { - let config = Config::new(); + let config = Config::new(""); let root = config.find_template("a.html", None); let path = config.find_template("sub/b.html", Some(&root)); assert_eq_rooted(&path, "sub/b.html"); @@ -128,14 +211,14 @@ mod tests { #[test] #[should_panic] fn find_relative_nonexistent() { - let config = Config::new(); + let config = Config::new(""); let root = config.find_template("a.html", None); config.find_template("b.html", Some(&root)); } #[test] fn find_relative() { - let config = Config::new(); + let config = Config::new(""); let root = config.find_template("sub/b.html", None); let path = config.find_template("c.html", Some(&root)); assert_eq_rooted(&path, "sub/c.html"); @@ -143,9 +226,105 @@ mod tests { #[test] fn find_relative_sub() { - let config = Config::new(); + let config = Config::new(""); let root = config.find_template("sub/b.html", None); let path = config.find_template("sub1/d.html", Some(&root)); assert_eq_rooted(&path, "sub/sub1/d.html"); } + + #[test] + fn add_syntax() { + let raw_config = r#" + default_syntax = "foo" + + [[syntax]] + name = "foo" + block_start = "~<" + + [[syntax]] + name = "bar" + expr_start = "%%" + "#; + + let default_syntax = Syntax::default(); + let config = Config::new(raw_config); + assert_eq!(config.default_syntax, "foo"); + + let foo = config.syntaxes.get("foo").unwrap(); + assert_eq!(foo.block_start, "~<"); + assert_eq!(foo.block_end, default_syntax.block_end); + assert_eq!(foo.expr_start, default_syntax.expr_start); + assert_eq!(foo.expr_end, default_syntax.expr_end); + assert_eq!(foo.comment_start, default_syntax.comment_start); + assert_eq!(foo.comment_end, default_syntax.comment_end); + + let bar = config.syntaxes.get("bar").unwrap(); + assert_eq!(bar.block_start, default_syntax.block_start); + assert_eq!(bar.block_end, default_syntax.block_end); + assert_eq!(bar.expr_start, "%%"); + assert_eq!(bar.expr_end, default_syntax.expr_end); + assert_eq!(bar.comment_start, default_syntax.comment_start); + assert_eq!(bar.comment_end, default_syntax.comment_end); + } + + #[test] + fn add_syntax_two() { + let raw_config = r#" + default_syntax = "foo" + + syntax = [{ name = "foo", block_start = "~<" }, + { name = "bar", expr_start = "%%" } ] + "#; + + let default_syntax = Syntax::default(); + let config = Config::new(raw_config); + assert_eq!(config.default_syntax, "foo"); + + let foo = config.syntaxes.get("foo").unwrap(); + assert_eq!(foo.block_start, "~<"); + assert_eq!(foo.block_end, default_syntax.block_end); + assert_eq!(foo.expr_start, default_syntax.expr_start); + assert_eq!(foo.expr_end, default_syntax.expr_end); + assert_eq!(foo.comment_start, default_syntax.comment_start); + assert_eq!(foo.comment_end, default_syntax.comment_end); + + let bar = config.syntaxes.get("bar").unwrap(); + assert_eq!(bar.block_start, default_syntax.block_start); + assert_eq!(bar.block_end, default_syntax.block_end); + assert_eq!(bar.expr_start, "%%"); + assert_eq!(bar.expr_end, default_syntax.expr_end); + assert_eq!(bar.comment_start, default_syntax.comment_start); + assert_eq!(bar.comment_end, default_syntax.comment_end); + } + + #[should_panic] + #[test] + fn use_default_at_syntax_name() { + let raw_config = r#" + syntax = [{ name = "default" }] + "#; + + let _config = Config::new(raw_config); + } + + #[should_panic] + #[test] + fn duplicated_syntax_name_on_list() { + let raw_config = r#" + syntax = [{ name = "foo", block_start = "~<" }, + { name = "foo", block_start = "%%" } ] + "#; + + let _config = Config::new(raw_config); + } + + #[should_panic] + #[test] + fn is_not_exist_default_syntax() { + let raw_config = r#" + default_syntax = "foo" + "#; + + let _config = Config::new(raw_config); + } } -- cgit