Add list_variables method

This commit is contained in:
2021-05-27 22:52:24 +02:00
parent 0af5d72005
commit 6d382f9a34
6 changed files with 104 additions and 23 deletions

2
Cargo.lock generated
View File

@ -46,7 +46,7 @@ dependencies = [
[[package]]
name = "blueprint"
version = "0.3.0"
version = "0.4.0"
dependencies = [
"nom",
"structopt",

View File

@ -1,6 +1,6 @@
[package]
name = "blueprint"
version = "0.3.0"
version = "0.4.0"
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
description = "A simple templating library"
edition = "2018"

View File

@ -53,10 +53,23 @@ impl Template<'_> {
}
Ok(())
}
pub fn list_variables(&self) -> Vec<&str> {
let mut vars: Vec<_> = self
.parts
.iter()
.flat_map(|part| part.list_variables())
.collect();
vars.sort_unstable();
vars.dedup();
vars
}
}
impl Part<'_> {
pub fn write(&self, env: &Env, w: &mut impl Write) -> io::Result<()> {
fn write(&self, env: &Env, w: &mut impl Write) -> io::Result<()> {
match self {
Part::Text(text) => w.write_all(text.as_bytes())?,
Part::If {
@ -81,10 +94,34 @@ impl Part<'_> {
}
Ok(())
}
fn list_variables(&self) -> Vec<&str> {
match self {
Part::Text(_) => vec![],
Part::If {
if_then,
elifs,
else_part,
} => {
let if_then = if_then.list_variables().into_iter();
let elifs = elifs.iter().flat_map(|elif| elif.list_variables());
if let Some(else_part) = else_part {
if_then
.chain(elifs)
.chain(else_part.list_variables())
.collect()
} else {
if_then.chain(elifs).collect()
}
}
}
}
}
impl IfThen<'_> {
pub fn write(&self, env: &Env, w: &mut impl Write) -> io::Result<bool> {
fn write(&self, env: &Env, w: &mut impl Write) -> io::Result<bool> {
let cond = self.cond.eval(env);
let cond = match &*cond {
&Value::Bool(cond) => cond,
@ -100,10 +137,18 @@ impl IfThen<'_> {
Ok(cond)
}
fn list_variables(&self) -> Vec<&str> {
self.parts
.iter()
.flat_map(|part| part.list_variables())
.chain(self.cond.list_variables().into_iter())
.collect()
}
}
impl Expr<'_> {
pub fn eval<'a>(&self, env: &'a Env) -> Cow<'a, Value> {
fn eval<'a>(&self, env: &'a Env) -> Cow<'a, Value> {
match self {
Expr::Variable(var) => Cow::Borrowed(env.get(&**var).unwrap_or(&Value::Null)),
Expr::Str(s) => Cow::Owned(Value::Str(s.to_string())),
@ -136,4 +181,16 @@ impl Expr<'_> {
}
}
}
fn list_variables(&self) -> Vec<&str> {
match self {
Expr::Variable(var) => vec![var],
Expr::Str(_) => vec![],
Expr::Cmp { expr1, expr2, .. } => expr1
.list_variables()
.into_iter()
.chain(expr2.list_variables())
.collect(),
}
}
}

View File

@ -173,4 +173,15 @@ mod test {
assert_eq!(ast, expected);
}
#[test]
fn test_list_vars() {
let input = "one {% if foo %} two {% if bar %} three {% end %} four {% end %} five";
let ast = parse_template(&input).expect("failed to parse");
let expected = vec!["bar", "foo"];
let actual = ast.list_variables();
assert_eq!(actual, expected);
}
}

View File

@ -5,17 +5,19 @@ use std::path::PathBuf;
use structopt::StructOpt;
#[derive(StructOpt)]
struct Opt {
template: PathBuf,
env: Vec<String>,
enum Opt {
Eval { template: PathBuf, env: Vec<String> },
ListVars { template: PathBuf },
}
fn main() {
let opt = Opt::from_args();
let file = read_to_string(&opt.template).expect("failed to load file");
match opt {
Opt::Eval { template, env } => {
let file = read_to_string(template).expect("failed to load file");
let template = blueprint::parse_template(&file).expect("failed to parse");
let env = opt
.env
let env = env
.into_iter()
.map(|key| (key, Value::Bool(true)))
.collect();
@ -25,3 +27,14 @@ fn main() {
.write(&env, &mut stdout)
.expect("failed to write output");
}
Opt::ListVars { template } => {
let file = read_to_string(template).expect("failed to load file");
let template = blueprint::parse_template(&file).expect("failed to parse");
for var in template.list_variables() {
println!("{}", var);
}
}
}
}

View File

@ -86,7 +86,7 @@ fn text_part(i: &str) -> IResult<&str, Part> {
//eprintln!("text_part 2 \"{}\"", i);
let (i, o) = r.unwrap_or(("", i));
//eprintln!("text_part 3 \"{}\"", i);
if o == "" {
if o.is_empty() {
//eprintln!("text_part 4A \"{}\"", i);
Err(nom::Err::Error(nom::error::Error::new(
i,
@ -168,11 +168,11 @@ pub fn parse_template(template: &str) -> Result<Template, Error> {
Ok((rest, parts)) => {
//eprintln!("{:#?}", parts);
if rest != "" {
if rest.is_empty() {
Ok(Template { parts })
} else {
//panic!("failed to parse input. remainder: \"{}\"", rest);
Err(Error::ParseError)
} else {
Ok(Template { parts })
}
}
Err(_e) => {