Add list_variables method
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -46,7 +46,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "blueprint"
|
||||
version = "0.3.0"
|
||||
version = "0.4.0"
|
||||
dependencies = [
|
||||
"nom",
|
||||
"structopt",
|
||||
|
||||
@ -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"
|
||||
|
||||
63
src/ast.rs
63
src/ast.rs
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
11
src/lib.rs
11
src/lib.rs
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
41
src/main.rs
41
src/main.rs
@ -5,23 +5,36 @@ 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");
|
||||
let template = blueprint::parse_template(&file).expect("failed to parse");
|
||||
let env = opt
|
||||
.env
|
||||
.into_iter()
|
||||
.map(|key| (key, Value::Bool(true)))
|
||||
.collect();
|
||||
|
||||
let mut stdout = io::stdout();
|
||||
template
|
||||
.write(&env, &mut stdout)
|
||||
.expect("failed to write output");
|
||||
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 = env
|
||||
.into_iter()
|
||||
.map(|key| (key, Value::Bool(true)))
|
||||
.collect();
|
||||
|
||||
let mut stdout = io::stdout();
|
||||
template
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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) => {
|
||||
|
||||
Reference in New Issue
Block a user