aboutsummaryrefslogtreecommitdiffstats
path: root/askama_shared/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--askama_shared/src/lib.rs223
1 files changed, 201 insertions, 22 deletions
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<PathBuf>,
+ pub syntaxes: BTreeMap<String, Syntax<'a>>,
+ 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<RawSyntax<'a>> 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<String, Syntax> = 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<General>,
+ syntax: Option<Vec<RawSyntax<'d>>>,
+ default_syntax: Option<&'d str>,
}
#[derive(Deserialize)]
@@ -85,11 +156,23 @@ struct General {
dirs: Option<Vec<String>>,
}
+#[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);
+ }
}