From 5f0b04d5f3b13f0913622d8f0e6946f620041602 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 18 Apr 2017 16:46:23 -0500 Subject: [PATCH] feat(debug): Adding CLI for testing liquid Note: this relies on a nightly Cargo.toml feature `required-features`. Under stable, you will see a warning about an unsupported option and then the build will fail due to a missing `clap` unless you specify `--features=cli`. --- .travis.yml | 6 +-- Cargo.toml | 26 +++++++---- appveyor.yml | 4 +- src/main.rs | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 src/main.rs diff --git a/.travis.yml b/.travis.yml index b655b0462..a3e579b5f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,9 @@ install: - export PATH=$HOME/.cargo/bin:$PATH script: - - cargo build - - cargo test - - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then (cargo clippy) fi + - cargo build --features=cli + - cargo test --features=cli + - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then (cargo clippy --features=cli) fi - cargo fmt -- --write-mode=diff addons: diff --git a/Cargo.toml b/Cargo.toml index 502f233a5..e594ebf1d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [package] - name = "liquid" version = "0.9.1" authors = ["Johann Hofmann "] @@ -13,22 +12,33 @@ license = "MIT" build = "build.rs" +[[bin]] +name = "liquid-dbg" +required-features = ["cli"] +test = false +doctest = false +bench = false +doc = false + +[features] +default = ["extra-filters"] +cli = ["clap", "error-chain", "toml"] +extra-filters = [] +dev = [] + [dependencies] regex = "0.2" lazy_static = "0.2" chrono = "0.3" unicode-segmentation = "1.1" +clap = { version = "2.22.0", optional = true } +error-chain = { version = "0.10.0", optional = true } +toml = { version = "0.3.2", optional = true } + [build-dependencies] skeptic = "0.9" [dev-dependencies] difference = "1.0" skeptic = "0.9" - -[features] -default = ["extra-filters"] - -extra-filters = [] - -dev=[] diff --git a/appveyor.yml b/appveyor.yml index ca050810c..70cf1f3fe 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,8 +27,8 @@ install: - cargo -V test_script: - - cargo build --verbose - - cargo test + - cargo build --verbose --features=cli + - cargo test --features=cli cache: - C:\Users\appveyor\.cargo\registry diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 000000000..963e5e1b4 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,122 @@ +#[macro_use] +extern crate clap; +#[macro_use] +extern crate error_chain; +extern crate liquid; +extern crate toml; + +use std::collections; +use std::fs; +use std::io; +use std::io::{Write, Read}; +use std::path; +use liquid::Renderable; + +macro_rules! println_stderr( + ($($arg:tt)*) => { { + let r = writeln!(&mut ::std::io::stderr(), $($arg)*); + r.expect("failed printing to stderr"); + } } +); + +error_chain! { + + links { + } + + foreign_links { + Clap(clap::Error); + Io(io::Error); + Liquid(liquid::Error); + Toml(toml::de::Error); + } + + errors { + } +} + +fn option<'a>(name: &'a str, value: &'a str) -> clap::Arg<'a, 'a> { + clap::Arg::with_name(name).long(name).value_name(value) +} + +fn convert_value(toml_value: &toml::Value) -> Result { + match *toml_value { + toml::Value::String(ref s) => Ok(liquid::Value::Str(s.to_string())), + toml::Value::Integer(n) => Ok(liquid::Value::Num(n as f32)), + toml::Value::Float(n) => Ok(liquid::Value::Num(n as f32)), + toml::Value::Boolean(b) => Ok(liquid::Value::Bool(b)), + toml::Value::Datetime(_) => Err("Datetime's are unsupported".into()), + toml::Value::Array(ref a) => { + let liquid_array: Result> = a.iter().map(convert_value).collect(); + let liquid_array = liquid_array?; + Ok(liquid::Value::Array(liquid_array)) + } + toml::Value::Table(ref t) => { + let liquid_object: Result> = t.iter() + .map(|(k, v)| { + let v = convert_value(v); + match v { + Ok(v) => Ok((k.to_string(), v)), + Err(e) => Err(e), + } + }) + .collect(); + let liquid_object = liquid_object?; + Ok(liquid::Value::Object(liquid_object)) + } + } +} + +fn build_context(path: &path::Path) -> Result { + let mut input = String::new(); + let mut f = fs::File::open(path)?; + f.read_to_string(&mut input)?; + let input: toml::Value = input.parse()?; + let value = convert_value(&input)?; + let value = match value { + liquid::Value::Object(o) => Ok(o), + _ => Err("File must be a toml table"), + }?; + let data = liquid::Context::with_values(value); + + Ok(data) +} + +fn run() -> Result<()> { + let matches = clap::App::new("liquidate").version(crate_version!()) + .author(crate_authors!()) + .arg(option("input", "LIQUID").required(true)) + .arg(option("output", "TXT")) + .arg(option("context", "TOML")) + .arg(option("include-root", "PATH")) + .get_matches_safe()?; + + let mut options = liquid::LiquidOptions::default(); + options.file_system = matches.value_of("include-root").map(path::PathBuf::from); + + let mut data = matches.value_of("context") + .map(|s| { + let p = path::PathBuf::from(s); + build_context(p.as_path()) + }) + .map_or(Ok(None), |r| r.map(Some))? + .unwrap_or_else(liquid::Context::new); + + let template_path = + matches.value_of("input").map(path::PathBuf::from).expect("Parameter was required"); + let template = liquid::parse_file(template_path, options)?; + let output = template.render(&mut data)?.unwrap_or_else(|| "".to_string()); + match matches.value_of("output") { + Some(path) => { + let mut out = fs::File::create(path::PathBuf::from(path))?; + out.write_all(output.as_bytes())?; + } + None => { + println!("{}", output); + } + } + + Ok(()) +} + +quick_main!(run);