diff --git a/src/build/mod.rs b/src/build/mod.rs index 0836304..ee15447 100644 --- a/src/build/mod.rs +++ b/src/build/mod.rs @@ -302,7 +302,7 @@ pub struct Builder<'a> { root: PathBuf, curr_file: Option<&'a str>, validate_mode: bool, - assert_collector: AssertCollector, + pub assert_collector: AssertCollector, env: Rc, // NOTE(jwall): We use interior mutability here because we need // our asset cache to be shared by multiple different sub-builders. @@ -477,7 +477,6 @@ impl<'a> Builder<'a> { /// Builds a ucg file at the named path. pub fn build_file(&mut self, name: &'a str) -> BuildResult { - eprintln!("building ucg file {}", name); self.curr_file = Some(name); let mut f = try!(File::open(name)); let mut s = String::new(); diff --git a/src/main.rs b/src/main.rs index c1cdb1b..af48233 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,14 +16,15 @@ extern crate clap; extern crate ucglib; use std::cell::RefCell; +use std::error::Error; use std::fs::File; use std::io; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; use std::process; use std::rc::Rc; use ucglib::build; -use ucglib::build::assets::MemoryCache; +use ucglib::build::assets::{Cache, MemoryCache}; use ucglib::build::Val; use ucglib::convert::traits; use ucglib::convert::ConverterRunner; @@ -43,11 +44,17 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> { ) (@subcommand build => (about: "Build a list of ucg files.") - (@arg INPUT: ... +required "Input ucg files to build.") + (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse in directories or not.") + (@arg INPUT: ... "Input ucg files or directories to build. If not provided then build the contents of the current directory.") ) (@subcommand validate => (about: "Check a list of ucg files for errors and run assertions.") - (@arg INPUT: ... +required "Input ucg files to validate.") + (@arg recurse: -r +required conflicts_with[INPUT] "Whether we should recurse or not.") + (@arg INPUT: ... "Input ucg files or directories to validate. If not provided scan the director for files with _test.ucg") + ) + (@subcommand converters => + (about: "list the available converters") + (@arg name: -c --converter-name +takes_value "Optionally print help for the provided converter") ) ).get_matches() } @@ -65,9 +72,108 @@ fn run_converter(c: ConverterRunner, v: Rc, f: Option<&str>) -> traits::Res c.convert(v, file) } +fn build_file( + file: &str, + validate: bool, + cache: Rc>, +) -> Result> { + let root = PathBuf::from(file); + let mut builder = build::Builder::new(root.parent().unwrap(), cache); + if validate { + builder.enable_validate_mode(); + } + try!(builder.build_file(file)); + if validate { + println!("{}", builder.assert_collector.summary); + } + Ok(builder) +} + +fn do_validate(file: &str, cache: Rc>) -> bool { + match build_file(file, true, cache) { + Ok(_) => println!("File {} validates", file), + Err(msg) => { + eprintln!("Err 2: {}", msg); + return false; + } + } + return true; +} + +fn do_compile(file: &str, cache: Rc>) -> bool { + println!("Building {}", file); + let builder = match build_file(file, false, cache.clone()) { + Ok(builder) => builder, + Err(err) => { + eprintln!("{:?}", err); + return false; + } + }; + let (typ, val) = match builder.out_lock { + Some((ref typ, ref val)) => (typ, val.clone()), + None => { + eprintln!("Build results in no value."); + return false; + } + }; + match ConverterRunner::new(typ) { + Ok(converter) => { + run_converter(converter, val, Some(file)).unwrap(); + eprintln!("Build successful"); + process::exit(0); + } + Err(msg) => { + eprintln!("{}", msg); + return false; + } + } +} + +fn visit_ucg_files( + path: &Path, + recurse: bool, + validate: bool, + cache: Rc>, +) -> Result> { + let our_path = String::from(path.to_string_lossy()); + let mut result = true; + if path.is_dir() { + for entry in try!(std::fs::read_dir(path)) { + let next_item = try!(entry); + let next_path = next_item.path(); + let path_as_string = String::from(next_path.to_string_lossy()); + if next_path.is_dir() && recurse { + if let Err(msg) = visit_ucg_files(&next_path, recurse, validate, cache.clone()) { + eprintln!("Err 1: {}", msg); + result = false; + } + } else { + if validate && path_as_string.ends_with("_test.ucg") { + if !do_validate(&path_as_string, cache.clone()) { + result = false; + } + } else { + if !do_compile(&path_as_string, cache.clone()) { + result = false; + } + } + } + } + } else if validate && our_path.ends_with("_test.ucg") { + if !do_validate(&our_path, cache) { + result = false; + } + } else { + if !do_compile(&our_path, cache) { + result = false; + } + } + Ok(result) +} + fn main() { let app = do_flags(); - let cache = Rc::new(RefCell::new(MemoryCache::new())); + let cache: Rc> = Rc::new(RefCell::new(MemoryCache::new())); if let Some(matches) = app.subcommand_matches("inspect") { let file = matches.value_of("INPUT").unwrap(); let sym = matches.value_of("sym"); @@ -105,42 +211,65 @@ fn main() { } } } else if let Some(matches) = app.subcommand_matches("build") { - let files = matches.values_of("INPUT").unwrap(); - for file in files { - let root = PathBuf::from(file); - let mut builder = build::Builder::new(root.parent().unwrap(), cache); - let result = builder.build_file(file); - if !result.is_ok() { - eprintln!("{:?}", result.err().unwrap()); - process::exit(1); + let files = matches.values_of("INPUT"); + let recurse = matches.is_present("recurse"); + let mut ok = true; + if files.is_none() { + let curr_dir = std::env::current_dir().unwrap(); + let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone()); + if let Ok(false) = ok { + process::exit(1) } - let (typ, val) = match builder.out_lock { - Some((ref typ, ref val)) => (typ, val.clone()), - None => { - eprintln!("Build results in no value."); - process::exit(1); - } - }; - match ConverterRunner::new(typ) { - Ok(converter) => { - run_converter(converter, val, Some(file)).unwrap(); - eprintln!("Build successful"); - process::exit(0); - } - Err(msg) => { - eprintln!("{}", msg); - process::exit(1); - } + } + for file in files.unwrap() { + let pb = PathBuf::from(file); + if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone()) { + ok = false; } } + if !ok { + process::exit(1) + } } else if let Some(matches) = app.subcommand_matches("validate") { - let files = matches.values_of("INPUT").unwrap(); - let mut builder = build::Builder::new(std::env::current_dir().unwrap(), cache); - builder.enable_validate_mode(); - for file in files { - builder.build_file(file).unwrap(); - println!("File Validates"); + let files = matches.values_of("INPUT"); + let recurse = matches.is_present("recurse"); + if files.is_none() { + let curr_dir = std::env::current_dir().unwrap(); + let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone()); + if let Ok(false) = ok { + process::exit(1) + } + } else { + let mut ok = true; + for file in files.unwrap() { + let pb = PathBuf::from(file); + if pb.is_dir() { + if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, cache.clone()) { + ok = false; + } + } else { + match build_file(file, true, cache.clone()) { + Ok(b) => { + if b.assert_collector.success { + println!("File {} Validates", file); + } else { + println!("File {} Fails", file); + ok = false; + } + } + Err(msg) => { + // We continue to process the other files despite this failure. + eprintln!("{}", msg); + ok = false; + } + } + } + } + if !ok { + process::exit(1) + } } process::exit(0); + } else if let Some(_todo) = app.subcommand_matches("converters") { } }