FEATURE: Support limited help for the available converters.

This commit is contained in:
Jeremy Wall 2018-08-22 00:13:11 -05:00
parent f9cdd4f3f6
commit 398fbc7e1b
7 changed files with 78 additions and 57 deletions

View File

@ -91,4 +91,8 @@ impl Converter for EnvConverter {
fn file_ext(&self) -> String { fn file_ext(&self) -> String {
String::from("env") String::from("env")
} }
fn description(&self) -> String {
"Convert ucg Vals into environment variables.".to_string()
}
} }

View File

@ -186,6 +186,10 @@ impl Converter for ExecConverter {
fn file_ext(&self) -> String { fn file_ext(&self) -> String {
String::from("sh") String::from("sh")
} }
fn description(&self) -> String {
"Convert ucg Vals into an bash script with \nenvironment variables set and command line arguments sent..".to_string()
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -111,6 +111,10 @@ impl Converter for FlagConverter {
fn file_ext(&self) -> String { fn file_ext(&self) -> String {
String::from("txt") String::from("txt")
} }
fn description(&self) -> String {
"Convert ucg Vals into command line flags.".to_string()
}
} }
// We need some unit tests for this now :D // We need some unit tests for this now :D

View File

@ -92,4 +92,8 @@ impl Converter for JsonConverter {
fn file_ext(&self) -> String { fn file_ext(&self) -> String {
String::from("json") String::from("json")
} }
fn description(&self) -> String {
"Convert ucg Vals into valid json.".to_string()
}
} }

View File

@ -19,54 +19,46 @@ pub mod flags;
pub mod json; pub mod json;
pub mod traits; pub mod traits;
use std::io::Write; use std::collections::HashMap;
use std::rc::Rc;
use build::Val;
/// ConverterRunner knows how to run a given converter on a Val. /// ConverterRunner knows how to run a given converter on a Val.
pub struct ConverterRunner { pub struct ConverterRegistry {
converter: Box<traits::Converter>, converters: HashMap<String, Box<traits::Converter>>,
} }
impl ConverterRunner { impl ConverterRegistry {
/// new creates a new ConverterRunner with a converter for the provided output target. /// new creates a new ConverterRunner with a converter for the provided output target.
/// ///
/// * flags /// * flags
/// * json /// * json
/// * env /// * env
/// * exec /// * exec
pub fn new(typ: &str) -> Result<Self, String> { fn new() -> Self {
if typ == "flags" { ConverterRegistry {
return Ok(ConverterRunner { converters: HashMap::new(),
converter: Box::new(flags::FlagConverter::new()),
});
} }
if typ == "json" {
return Ok(ConverterRunner {
converter: Box::new(json::JsonConverter::new()),
});
}
if typ == "env" {
return Ok(ConverterRunner {
converter: Box::new(env::EnvConverter::new()),
});
}
if typ == "exec" {
return Ok(ConverterRunner {
converter: Box::new(exec::ExecConverter::new()),
});
}
return Err(format!("Unknown Target output type: {}", typ));
} }
/// convert runs the Converter on a Val and writes the output to the provided writer. pub fn make_registry() -> Self {
pub fn convert(&self, v: Rc<Val>, mut w: Box<Write>) -> traits::Result { let mut registry = Self::new();
self.converter.convert(v, &mut w) registry.register("json", Box::new(json::JsonConverter::new()));
registry.register("env", Box::new(env::EnvConverter::new()));
registry.register("flags", Box::new(flags::FlagConverter::new()));
registry.register("exec", Box::new(exec::ExecConverter::new()));
registry
} }
/// ext returns the expected file extension for this conversion. pub fn register<S: Into<String>>(&mut self, typ: S, converter: Box<traits::Converter>) {
pub fn ext(&self) -> String { self.converters.insert(typ.into(), converter);
self.converter.file_ext()
} }
pub fn get_converter(&self, typ: &str) -> Option<&traits::Converter> {
self.converters.get(typ).map(|c| c.as_ref())
}
pub fn get_converter_list(&self) -> Vec<(&String, &Box<traits::Converter>)> {
self.converters.iter().collect()
}
// TODO(jwall): Support converter help descriptions.
} }

View File

@ -27,4 +27,5 @@ pub type Result = result::Result<(), Box<Error>>;
pub trait Converter { pub trait Converter {
fn convert(&self, vs: Rc<Val>, w: &mut Write) -> Result; fn convert(&self, vs: Rc<Val>, w: &mut Write) -> Result;
fn file_ext(&self) -> String; fn file_ext(&self) -> String;
fn description(&self) -> String;
} }

View File

@ -27,7 +27,7 @@ use ucglib::build;
use ucglib::build::assets::{Cache, 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::ConverterRegistry;
// TODO(jwall): List the target output types automatically. // TODO(jwall): List the target output types automatically.
fn do_flags<'a>() -> clap::ArgMatches<'a> { fn do_flags<'a>() -> clap::ArgMatches<'a> {
@ -54,22 +54,21 @@ fn do_flags<'a>() -> clap::ArgMatches<'a> {
) )
(@subcommand converters => (@subcommand converters =>
(about: "list the available converters") (about: "list the available converters")
(@arg name: -c --converter-name +takes_value "Optionally print help for the provided converter")
) )
).get_matches() ).get_matches()
} }
fn run_converter(c: ConverterRunner, v: Rc<Val>, f: Option<&str>) -> traits::Result { fn run_converter(c: &traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::Result {
let file: Box<std::io::Write> = match f { let mut file: Box<std::io::Write> = match f {
Some(f) => { Some(f) => {
let mut path_buf = PathBuf::from(f); let mut path_buf = PathBuf::from(f);
path_buf.set_extension(c.ext()); path_buf.set_extension(c.file_ext());
let new_path = path_buf.to_str().unwrap(); let new_path = path_buf.to_str().unwrap();
Box::new(try!(File::create(&new_path))) Box::new(try!(File::create(&new_path)))
} }
None => Box::new(io::stdout()), None => Box::new(io::stdout()),
}; };
c.convert(v, file) c.convert(v, file.as_mut())
} }
fn build_file( fn build_file(
@ -101,7 +100,7 @@ fn do_validate(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
return true; return true;
} }
fn do_compile(file: &str, cache: Rc<RefCell<Cache>>) -> bool { fn do_compile(file: &str, cache: Rc<RefCell<Cache>>, registry: &ConverterRegistry) -> bool {
println!("Building {}", file); println!("Building {}", file);
let builder = match build_file(file, false, cache.clone()) { let builder = match build_file(file, false, cache.clone()) {
Ok(builder) => builder, Ok(builder) => builder,
@ -117,14 +116,14 @@ fn do_compile(file: &str, cache: Rc<RefCell<Cache>>) -> bool {
return false; return false;
} }
}; };
match ConverterRunner::new(typ) { match registry.get_converter(typ) {
Ok(converter) => { Some(converter) => {
run_converter(converter, val, Some(file)).unwrap(); run_converter(converter, val, Some(file)).unwrap();
eprintln!("Build successful"); eprintln!("Build successful");
process::exit(0); process::exit(0);
} }
Err(msg) => { None => {
eprintln!("{}", msg); eprintln!("No such converter {}", typ);
return false; return false;
} }
} }
@ -135,6 +134,7 @@ fn visit_ucg_files(
recurse: bool, recurse: bool,
validate: bool, validate: bool,
cache: Rc<RefCell<Cache>>, cache: Rc<RefCell<Cache>>,
registry: &ConverterRegistry,
) -> Result<bool, Box<Error>> { ) -> Result<bool, Box<Error>> {
let our_path = String::from(path.to_string_lossy()); let our_path = String::from(path.to_string_lossy());
let mut result = true; let mut result = true;
@ -146,7 +146,9 @@ fn visit_ucg_files(
let next_path = next_item.path(); let next_path = next_item.path();
let path_as_string = String::from(next_path.to_string_lossy()); let path_as_string = String::from(next_path.to_string_lossy());
if next_path.is_dir() && recurse { if next_path.is_dir() && recurse {
if let Err(msg) = visit_ucg_files(&next_path, recurse, validate, cache.clone()) { if let Err(msg) =
visit_ucg_files(&next_path, recurse, validate, cache.clone(), registry)
{
eprintln!("Err 1: {}", msg); eprintln!("Err 1: {}", msg);
result = false; result = false;
} }
@ -159,7 +161,7 @@ fn visit_ucg_files(
summary.push_str(format!("{} - PASS\n", path_as_string).as_str()) summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
} }
} else if !validate { } else if !validate {
if !do_compile(&path_as_string, cache.clone()) { if !do_compile(&path_as_string, cache.clone(), registry) {
result = false; result = false;
} }
} }
@ -173,7 +175,7 @@ fn visit_ucg_files(
summary.push_str(format!("{} - PASS\n", &our_path).as_str()) summary.push_str(format!("{} - PASS\n", &our_path).as_str())
} }
} else if !validate { } else if !validate {
if !do_compile(&our_path, cache) { if !do_compile(&our_path, cache, registry) {
result = false; result = false;
} }
} }
@ -187,14 +189,15 @@ fn visit_ucg_files(
fn main() { fn main() {
let app = do_flags(); let app = do_flags();
let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new())); let cache: Rc<RefCell<Cache>> = Rc::new(RefCell::new(MemoryCache::new()));
let registry = ConverterRegistry::make_registry();
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");
let target = matches.value_of("target").unwrap(); let target = matches.value_of("target").unwrap();
let root = PathBuf::from(file); let root = PathBuf::from(file);
let mut builder = build::Builder::new(root.parent().unwrap(), cache); let mut builder = build::Builder::new(root.parent().unwrap(), cache);
match ConverterRunner::new(target) { match registry.get_converter(target) {
Ok(converter) => { Some(converter) => {
// TODO(jwall): We should warn if this is a test file. // TODO(jwall): We should warn if this is a test file.
let result = builder.build_file(file); let result = builder.build_file(file);
if !result.is_ok() { if !result.is_ok() {
@ -218,8 +221,8 @@ fn main() {
} }
} }
} }
Err(msg) => { None => {
eprintln!("{}", msg); eprintln!("No such converter {}", target);
process::exit(1); process::exit(1);
} }
} }
@ -229,14 +232,14 @@ fn main() {
let mut ok = true; let mut ok = true;
if files.is_none() { if files.is_none() {
let curr_dir = std::env::current_dir().unwrap(); let curr_dir = std::env::current_dir().unwrap();
let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone()); let ok = visit_ucg_files(curr_dir.as_path(), recurse, false, cache.clone(), &registry);
if let Ok(false) = ok { if let Ok(false) = ok {
process::exit(1) process::exit(1)
} }
} }
for file in files.unwrap() { for file in files.unwrap() {
let pb = PathBuf::from(file); let pb = PathBuf::from(file);
if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone()) { if let Ok(false) = visit_ucg_files(&pb, recurse, false, cache.clone(), &registry) {
ok = false; ok = false;
} }
} }
@ -248,7 +251,7 @@ fn main() {
let recurse = matches.is_present("recurse"); let recurse = matches.is_present("recurse");
if files.is_none() { if files.is_none() {
let curr_dir = std::env::current_dir().unwrap(); let curr_dir = std::env::current_dir().unwrap();
let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone()); let ok = visit_ucg_files(curr_dir.as_path(), recurse, true, cache.clone(), &registry);
if let Ok(false) = ok { if let Ok(false) = ok {
process::exit(1) process::exit(1)
} }
@ -257,7 +260,9 @@ fn main() {
for file in files.unwrap() { for file in files.unwrap() {
let pb = PathBuf::from(file); let pb = PathBuf::from(file);
if pb.is_dir() { if pb.is_dir() {
if let Ok(false) = visit_ucg_files(pb.as_path(), recurse, true, cache.clone()) { if let Ok(false) =
visit_ucg_files(pb.as_path(), recurse, true, cache.clone(), &registry)
{
ok = false; ok = false;
} }
} else { } else {
@ -284,6 +289,13 @@ fn main() {
} }
process::exit(0); process::exit(0);
} else if let Some(_todo) = app.subcommand_matches("converters") { } else if let Some(_todo) = app.subcommand_matches("converters") {
// TODO(jwall): Flesh this command out. println!("Available converters:");
println!("");
for (name, c) in registry.get_converter_list().iter() {
println!("- {}", name);
println!(" Description: {}", c.description());
println!(" Output Extension: `.{}`", c.file_ext());
println!("");
}
} }
} }