diff --git a/.travis.yml b/.travis.yml index b655b0462..589addd00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,11 @@ install: - export PATH=$HOME/.cargo/bin:$PATH script: - - cargo build - - cargo test - - if [ "$TRAVIS_RUST_VERSION" = "nightly" ]; then (cargo clippy) fi + - cargo check + - cargo check --features "cli" + - cargo check --features "cli serde_yaml serde_json" + - cargo test --features "cli serde_yaml serde_json" + - 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 1c3f59718..abbc1d8a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,4 @@ [package] - name = "liquid" version = "0.9.1" authors = ["Johann Hofmann "] @@ -13,6 +12,20 @@ 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", "serde_yaml"] +extra-filters = [] +dev = [] + [dependencies] regex = "0.2" lazy_static = "0.2" @@ -22,6 +35,11 @@ itertools = "0.6.0" serde = "1.0" serde_derive = "1.0" +clap = { version = "2.22.0", optional = true } +error-chain = { version = "0.10.0", optional = true } +serde_yaml = { version = "0.7", optional = true } +serde_json = { version = "1.0", optional = true } + [build-dependencies] skeptic = "0.9" @@ -29,10 +47,3 @@ skeptic = "0.9" difference = "1.0" skeptic = "0.9" serde_yaml = "0.7" - -[features] -default = ["extra-filters"] - -extra-filters = [] - -dev=[] diff --git a/appveyor.yml b/appveyor.yml index ca050810c..886fc5d54 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -27,8 +27,10 @@ install: - cargo -V test_script: - - cargo build --verbose - - cargo test + - cargo check --verbose + - cargo check --verbose --features "cli" + - cargo check --verbose --features "cli serde_yaml serde_json" + - cargo test --features "cli serde_yaml serde_json" cache: - C:\Users\appveyor\.cargo\registry diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 000000000..56be1728d --- /dev/null +++ b/src/main.rs @@ -0,0 +1,116 @@ +#[macro_use] +extern crate clap; +#[macro_use] +extern crate error_chain; +extern crate liquid; + +#[cfg(feature = "serde_yaml")] +extern crate serde_yaml; +#[cfg(feature = "serde_json")] +extern crate serde_json; + +use std::ffi; +use std::fs; +use std::io; +use std::io::Write; +use std::path; +use liquid::Renderable; + +error_chain! { + links { + } + + foreign_links { + Clap(clap::Error); + Io(io::Error); + Liquid(liquid::Error); + Yaml(serde_yaml::Error) #[cfg(feature = "serde_yaml")]; + Json(serde_json::Error) #[cfg(feature = "serde_json")]; + } + + errors { + } +} + +fn option<'a>(name: &'a str, value: &'a str) -> clap::Arg<'a, 'a> { + clap::Arg::with_name(name).long(name).value_name(value) +} + +#[cfg(feature = "serde_yaml")] +fn load_yaml(path: &path::Path) -> Result { + let f = fs::File::open(path)?; + serde_yaml::from_reader(f).map_err(|e| e.into()) +} + +#[cfg(not(feature = "serde_yaml"))] +fn load_yaml(path: &path::Path) -> Result { + bail!("yaml is unsupported"); +} + +#[cfg(feature = "serde_json")] +fn load_json(path: &path::Path) -> Result { + let f = fs::File::open(path)?; + serde_json::from_reader(f).map_err(|e| e.into()) +} + +#[cfg(not(feature = "serde_json"))] +fn load_json(path: &path::Path) -> Result { + bail!("json is unsupported"); +} + +fn build_context(path: &path::Path) -> Result { + let extension = path.extension().unwrap_or_else(|| ffi::OsStr::new("")); + let value = if extension == ffi::OsStr::new("yaml") { + load_yaml(path) + } else if extension == ffi::OsStr::new("yaml") { + load_json(path) + } else { + Err("Unsupported file type".into()) + }?; + let value = match value { + liquid::Value::Object(o) => Ok(o), + _ => Err("File must be an object"), + }?; + 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);