FEATURE: Handle directories and recursing.

The build and validate commands can nor process directories and recurse if desired.
This commit is contained in:
Jeremy Wall 2018-08-17 12:59:26 -05:00
parent 5b38e571ee
commit 3a90812d41
2 changed files with 166 additions and 38 deletions

View File

@ -302,7 +302,7 @@ pub struct Builder<'a> {
root: PathBuf, root: PathBuf,
curr_file: Option<&'a str>, curr_file: Option<&'a str>,
validate_mode: bool, validate_mode: bool,
assert_collector: AssertCollector, pub assert_collector: AssertCollector,
env: Rc<Val>, env: Rc<Val>,
// NOTE(jwall): We use interior mutability here because we need // NOTE(jwall): We use interior mutability here because we need
// our asset cache to be shared by multiple different sub-builders. // 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. /// Builds a ucg file at the named path.
pub fn build_file(&mut self, name: &'a str) -> BuildResult { pub fn build_file(&mut self, name: &'a str) -> BuildResult {
eprintln!("building ucg file {}", name);
self.curr_file = Some(name); self.curr_file = Some(name);
let mut f = try!(File::open(name)); let mut f = try!(File::open(name));
let mut s = String::new(); let mut s = String::new();

View File

@ -16,14 +16,15 @@ extern crate clap;
extern crate ucglib; extern crate ucglib;
use std::cell::RefCell; use std::cell::RefCell;
use std::error::Error;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::path::PathBuf; use std::path::{Path, PathBuf};
use std::process; use std::process;
use std::rc::Rc; use std::rc::Rc;
use ucglib::build; use ucglib::build;
use ucglib::build::assets::MemoryCache; use ucglib::build::assets::{Cache, MemoryCache};
use ucglib::build::Val; use ucglib::build::Val;
use ucglib::convert::traits; use ucglib::convert::traits;
use ucglib::convert::ConverterRunner; use ucglib::convert::ConverterRunner;
@ -43,11 +44,17 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> {
) )
(@subcommand build => (@subcommand build =>
(about: "Build a list of ucg files.") (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 => (@subcommand validate =>
(about: "Check a list of ucg files for errors and run assertions.") (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() ).get_matches()
} }
@ -65,9 +72,108 @@ fn run_converter(c: ConverterRunner, v: Rc<Val>, f: Option<&str>) -> traits::Res
c.convert(v, file) c.convert(v, file)
} }
fn build_file(
file: &str,
validate: bool,
cache: Rc<RefCell<Cache>>,
) -> Result<build::Builder, Box<Error>> {
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<RefCell<Cache>>) -> 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<RefCell<Cache>>) -> 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<RefCell<Cache>>,
) -> Result<bool, Box<Error>> {
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() { fn main() {
let app = do_flags(); let app = do_flags();
let cache = Rc::new(RefCell::new(MemoryCache::new())); let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
if let Some(matches) = app.subcommand_matches("inspect") { if let Some(matches) = app.subcommand_matches("inspect") {
let file = matches.value_of("INPUT").unwrap(); let file = matches.value_of("INPUT").unwrap();
let sym = matches.value_of("sym"); let sym = matches.value_of("sym");
@ -105,42 +211,65 @@ fn main() {
} }
} }
} else if let Some(matches) = app.subcommand_matches("build") { } else if let Some(matches) = app.subcommand_matches("build") {
let files = matches.values_of("INPUT").unwrap(); let files = matches.values_of("INPUT");
for file in files { let recurse = matches.is_present("recurse");
let root = PathBuf::from(file); let mut ok = true;
let mut builder = build::Builder::new(root.parent().unwrap(), cache); if files.is_none() {
let result = builder.build_file(file); let curr_dir = std::env::current_dir().unwrap();
if !result.is_ok() { let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone());
eprintln!("{:?}", result.err().unwrap()); if let Ok(false) = ok {
process::exit(1); process::exit(1)
} }
let (typ, val) = match builder.out_lock { }
Some((ref typ, ref val)) => (typ, val.clone()), for file in files.unwrap() {
None => { let pb = PathBuf::from(file);
eprintln!("Build results in no value."); if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone()) {
process::exit(1); ok = false;
}
};
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);
}
} }
} }
if !ok {
process::exit(1)
}
} else if let Some(matches) = app.subcommand_matches("validate") { } else if let Some(matches) = app.subcommand_matches("validate") {
let files = matches.values_of("INPUT").unwrap(); let files = matches.values_of("INPUT");
let mut builder = build::Builder::new(std::env::current_dir().unwrap(), cache); let recurse = matches.is_present("recurse");
builder.enable_validate_mode(); if files.is_none() {
for file in files { let curr_dir = std::env::current_dir().unwrap();
builder.build_file(file).unwrap(); let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone());
println!("File Validates"); 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); process::exit(0);
} else if let Some(_todo) = app.subcommand_matches("converters") {
} }
} }