Add list_variables method
This commit is contained in:
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -46,7 +46,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "blueprint"
|
name = "blueprint"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"nom",
|
"nom",
|
||||||
"structopt",
|
"structopt",
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "blueprint"
|
name = "blueprint"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
authors = ["Joakim Hulthe <joakim@hulthe.net>"]
|
||||||
description = "A simple templating library"
|
description = "A simple templating library"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|||||||
63
src/ast.rs
63
src/ast.rs
@ -53,10 +53,23 @@ impl Template<'_> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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<'_> {
|
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 {
|
match self {
|
||||||
Part::Text(text) => w.write_all(text.as_bytes())?,
|
Part::Text(text) => w.write_all(text.as_bytes())?,
|
||||||
Part::If {
|
Part::If {
|
||||||
@ -81,10 +94,34 @@ impl Part<'_> {
|
|||||||
}
|
}
|
||||||
Ok(())
|
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<'_> {
|
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 = self.cond.eval(env);
|
||||||
let cond = match &*cond {
|
let cond = match &*cond {
|
||||||
&Value::Bool(cond) => cond,
|
&Value::Bool(cond) => cond,
|
||||||
@ -100,10 +137,18 @@ impl IfThen<'_> {
|
|||||||
|
|
||||||
Ok(cond)
|
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<'_> {
|
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 {
|
match self {
|
||||||
Expr::Variable(var) => Cow::Borrowed(env.get(&**var).unwrap_or(&Value::Null)),
|
Expr::Variable(var) => Cow::Borrowed(env.get(&**var).unwrap_or(&Value::Null)),
|
||||||
Expr::Str(s) => Cow::Owned(Value::Str(s.to_string())),
|
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);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
25
src/main.rs
25
src/main.rs
@ -5,17 +5,19 @@ use std::path::PathBuf;
|
|||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
|
||||||
#[derive(StructOpt)]
|
#[derive(StructOpt)]
|
||||||
struct Opt {
|
enum Opt {
|
||||||
template: PathBuf,
|
Eval { template: PathBuf, env: Vec<String> },
|
||||||
env: Vec<String>,
|
ListVars { template: PathBuf },
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let opt = Opt::from_args();
|
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 template = blueprint::parse_template(&file).expect("failed to parse");
|
||||||
let env = opt
|
let env = env
|
||||||
.env
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|key| (key, Value::Bool(true)))
|
.map(|key| (key, Value::Bool(true)))
|
||||||
.collect();
|
.collect();
|
||||||
@ -25,3 +27,14 @@ fn main() {
|
|||||||
.write(&env, &mut stdout)
|
.write(&env, &mut stdout)
|
||||||
.expect("failed to write output");
|
.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);
|
//eprintln!("text_part 2 \"{}\"", i);
|
||||||
let (i, o) = r.unwrap_or(("", i));
|
let (i, o) = r.unwrap_or(("", i));
|
||||||
//eprintln!("text_part 3 \"{}\"", i);
|
//eprintln!("text_part 3 \"{}\"", i);
|
||||||
if o == "" {
|
if o.is_empty() {
|
||||||
//eprintln!("text_part 4A \"{}\"", i);
|
//eprintln!("text_part 4A \"{}\"", i);
|
||||||
Err(nom::Err::Error(nom::error::Error::new(
|
Err(nom::Err::Error(nom::error::Error::new(
|
||||||
i,
|
i,
|
||||||
@ -168,11 +168,11 @@ pub fn parse_template(template: &str) -> Result<Template, Error> {
|
|||||||
Ok((rest, parts)) => {
|
Ok((rest, parts)) => {
|
||||||
//eprintln!("{:#?}", parts);
|
//eprintln!("{:#?}", parts);
|
||||||
|
|
||||||
if rest != "" {
|
if rest.is_empty() {
|
||||||
|
Ok(Template { parts })
|
||||||
|
} else {
|
||||||
//panic!("failed to parse input. remainder: \"{}\"", rest);
|
//panic!("failed to parse input. remainder: \"{}\"", rest);
|
||||||
Err(Error::ParseError)
|
Err(Error::ParseError)
|
||||||
} else {
|
|
||||||
Ok(Template { parts })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_e) => {
|
Err(_e) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user