Add help text functionality for converters.

This commit is contained in:
Jeremy Wall 2019-03-25 20:23:50 -04:00
parent 263e91c9f9
commit a5fe59bb7c
10 changed files with 222 additions and 21 deletions

View File

@ -13,7 +13,8 @@
// limitations under the License. // limitations under the License.
//! Contains code for converting a UCG Val into the environment variable output target. //! Contains code for converting a UCG Val into the environment variable output target.
use std::io::Write; use std::fmt::Write as FmtWrite;
use std::io::Write as IOWrite;
use std::rc::Rc; use std::rc::Rc;
use crate::build::Val; use crate::build::Val;
@ -28,7 +29,7 @@ impl EnvConverter {
EnvConverter {} EnvConverter {}
} }
fn convert_tuple(&self, flds: &Vec<(String, Rc<Val>)>, w: &mut Write) -> ConvertResult { fn convert_tuple(&self, flds: &Vec<(String, Rc<Val>)>, w: &mut IOWrite) -> ConvertResult {
for &(ref name, ref val) in flds.iter() { for &(ref name, ref val) in flds.iter() {
if val.is_tuple() { if val.is_tuple() {
eprintln!("Skipping embedded tuple..."); eprintln!("Skipping embedded tuple...");
@ -44,12 +45,12 @@ impl EnvConverter {
Ok(()) Ok(())
} }
fn convert_list(&self, _items: &Vec<Rc<Val>>, _w: &mut Write) -> ConvertResult { fn convert_list(&self, _items: &Vec<Rc<Val>>, _w: &mut IOWrite) -> ConvertResult {
eprintln!("Skipping List..."); eprintln!("Skipping List...");
Ok(()) Ok(())
} }
fn write(&self, v: &Val, w: &mut Write) -> ConvertResult { fn write(&self, v: &Val, w: &mut IOWrite) -> ConvertResult {
match v { match v {
&Val::Empty => { &Val::Empty => {
// Empty is a noop. // Empty is a noop.
@ -91,7 +92,7 @@ impl EnvConverter {
} }
impl Converter for EnvConverter { impl Converter for EnvConverter {
fn convert(&self, v: Rc<Val>, mut w: &mut Write) -> ConvertResult { fn convert(&self, v: Rc<Val>, mut w: &mut IOWrite) -> ConvertResult {
self.write(&v, &mut w) self.write(&v, &mut w)
} }
@ -102,4 +103,21 @@ impl Converter for EnvConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into environment variables.".to_string() "Convert ucg Vals into environment variables.".to_string()
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(
h,
"Env conversions expect a tuple. With keys represent the variable name."
);
writeln!(h, "");
writeln!(h, "Allowed values can be:");
writeln!(h, "- Bool converts to true or false");
writeln!(h, "- Int");
writeln!(h, "- Float");
writeln!(h, "- String converted to a quoted string");
writeln!(h, "- Functions and Modules are ignored.");
h
}
} }

View File

@ -14,6 +14,7 @@
//! Contains code for converting a UCG Val into an executable script output target. //! Contains code for converting a UCG Val into an executable script output target.
use std; use std;
use std::fmt::Write as FmtWrite;
use std::io::{Cursor, Write}; use std::io::{Cursor, Write};
use std::rc::Rc; use std::rc::Rc;
@ -185,6 +186,40 @@ impl Converter for ExecConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into an bash script with \nenvironment variables set and command line arguments sent..".to_string() "Convert ucg Vals into an bash script with \nenvironment variables set and command line arguments sent..".to_string()
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(
h,
"Exec conversions expect a tuple with an expected set of keys."
);
writeln!(h, "");
writeln!(h, "The expected keys are:");
writeln!(h, "");
writeln!(h, "- command (string, required)");
writeln!(h, "\t The command to run in the script.");
writeln!(h, "");
writeln!(h, "- env (tuple, optional)");
writeln!(
h,
"\t Any environment variables that should be set in the script."
);
writeln!(
h,
"\t The env tuple is converted using the same rules as the env converter."
);
writeln!(h, "");
writeln!(h, "- args (tuple, optional");
writeln!(h, "\t Any command line arguments for the command line.");
writeln!(
h,
"\t The arguments are converted using the same rules as the flags converter."
);
writeln!(h, "");
writeln!(h, "- Functions and Modules are ignored.");
h
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -13,6 +13,7 @@
// limitations under the License. // limitations under the License.
//! Contains code for converting a UCG Val into the command line flag output target. //! Contains code for converting a UCG Val into the command line flag output target.
use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
@ -131,6 +132,37 @@ impl Converter for FlagConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into command line flags.".to_string() "Convert ucg Vals into command line flags.".to_string()
} }
}
// We need some unit tests for this now :D #[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(
h,
"Flags converts a tuple into a set of command line arguments for command line application."
);
writeln!(h, "");
writeln!(h, "The flags are converted using the following rules:");
writeln!(h, "");
writeln!(h, "- keys in a tuple are converted into the argument name.");
writeln!(
h,
"- values in a tuple are converted into the argument value."
);
writeln!(h, "- NULL values are not emitted");
writeln!(
h,
"- lists expand out into an argument for each item in the list."
);
writeln!(h, "\te.g. {{foo = [1, 2]}} becomes --foo=1 --foo=2");
writeln!(
h,
"- tuples expand out into an argument with the key as a prefix separated by a `.`."
);
writeln!(
h,
"\te.g. {{foo = {{bar = 1, baz = 2}}}} becomes --foo.bar=1 --foo.baz=2"
);
writeln!(h, "- Functions and Modules are ignored.");
h
}
}

View File

@ -10,6 +10,7 @@
//! Flags contains code for converting a UCG Val into the json output target. //! Flags contains code for converting a UCG Val into the json output target.
use std; use std;
use std::error::Error; use std::error::Error;
use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
@ -135,6 +136,24 @@ impl Converter for JsonConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into valid json.".to_string() "Convert ucg Vals into valid json.".to_string()
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(h, "JSON conversions expect any ucg value.");
writeln!(h, "");
writeln!(
h,
"They are transformed into json using the following rules:"
);
writeln!(h, "- NULL becomes null");
writeln!(h, "- tuples become objects {{}}");
writeln!(h, "- lists become lists []");
writeln!(h, "- Int and Float become numbers");
writeln!(h, "- Strings become strings.");
writeln!(h, "- Functions and Modules are ignored.");
h
}
} }
impl Importer for JsonConverter { impl Importer for JsonConverter {

View File

@ -66,8 +66,6 @@ impl ConverterRegistry {
pub fn get_converter_list(&self) -> Vec<(&String, &Box<traits::Converter>)> { pub fn get_converter_list(&self) -> Vec<(&String, &Box<traits::Converter>)> {
self.converters.iter().collect() self.converters.iter().collect()
} }
// TODO(jwall): Support converter help descriptions.
} }
pub struct ImporterRegistry { pub struct ImporterRegistry {
@ -109,6 +107,4 @@ impl ImporterRegistry {
pub fn get_importer_list(&self) -> Vec<(&String, &Box<dyn traits::Importer>)> { pub fn get_importer_list(&self) -> Vec<(&String, &Box<dyn traits::Importer>)> {
self.importers.iter().collect() self.importers.iter().collect()
} }
// TODO(jwall): Support converter help descriptions.
} }

View File

@ -15,6 +15,7 @@
use std; use std;
use std::error; use std::error;
use std::error::Error; use std::error::Error;
use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
@ -127,6 +128,28 @@ impl Converter for TomlConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into valid ucg.".to_string() "Convert ucg Vals into valid ucg.".to_string()
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(h, "TOML conversions expect any ucg value.");
writeln!(h, "");
writeln!(
h,
"They are transformed into toml using the following rules:"
);
writeln!(h, "- tuples become maps {{}}");
writeln!(h, "- lists become lists []");
writeln!(h, "- Int becomes an Int");
writeln!(h, "- Float becomes a Float");
writeln!(h, "- Strings become Strings.");
writeln!(
h,
"- NULL is not allowed in toml documents and will generate a compile error"
);
writeln!(h, "- Functions and Modules are ignored.");
h
}
} }
impl Importer for TomlConverter { impl Importer for TomlConverter {

View File

@ -30,6 +30,7 @@ pub trait Converter {
fn convert(&self, vs: Rc<Val>, w: &mut Write) -> ConvertResult; fn convert(&self, vs: Rc<Val>, w: &mut Write) -> ConvertResult;
fn file_ext(&self) -> String; fn file_ext(&self) -> String;
fn description(&self) -> String; fn description(&self) -> String;
fn help(&self) -> String;
} }
pub trait Importer { pub trait Importer {

View File

@ -14,6 +14,7 @@
use std; use std;
use std::error::Error; use std::error::Error;
use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
@ -234,4 +235,43 @@ impl Converter for XmlConverter {
fn description(&self) -> String { fn description(&self) -> String {
String::from("Convert a ucg DSL into xml.") String::from("Convert a ucg DSL into xml.")
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(h, "XML converts ucg tuples into xml documents.");
writeln!(h, "");
writeln!(h, "The tuple converts into xml using a declarative DSL.");
writeln!(h, "The top tuple describes the xml document:");
writeln!(
h,
"{{
version = \"1.1\" // Optional, Defaults to 1.1
encoding = \"utf-8\" // Optional, Defaults to UTF-8
standalone = true // Optional Defaults to false
root = {{ // Required defines the root element of the document.
name = \"top\",
}}
}};"
);
writeln!(h, "XML nodes are constructed like :");
writeln!(
h,
"{{
name = \"ns:element-name\",
ns = {{
prefix = \"myns\",
uri = \"http://example.org\",
}},
attrs = {{
id = \"foo\",
}},
children = [
// child elements go here.
],
}};"
);
writeln!(h, "Text nodes are just strings.");
h
}
} }

View File

@ -1,5 +1,6 @@
use std; use std;
use std::error::Error; use std::error::Error;
use std::fmt::Write as FmtWrite;
use std::io::Write; use std::io::Write;
use std::rc::Rc; use std::rc::Rc;
use std::result::Result; use std::result::Result;
@ -132,6 +133,24 @@ impl Converter for YamlConverter {
fn description(&self) -> String { fn description(&self) -> String {
"Convert ucg Vals into valid yaml.".to_string() "Convert ucg Vals into valid yaml.".to_string()
} }
#[allow(unused_must_use)]
fn help(&self) -> String {
let mut h = String::new();
writeln!(h, "YAML conversions expect any ucg value.");
writeln!(h, "");
writeln!(
h,
"They are transformed into toml using the following rules:"
);
writeln!(h, "- tuples become maps {{}}");
writeln!(h, "- lists become lists []");
writeln!(h, "- Int becomes an Int");
writeln!(h, "- Float becomes a Float");
writeln!(h, "- Strings become Strings.");
writeln!(h, "- Functions and Modules are ignored.");
h
}
} }
impl Importer for YamlConverter { impl Importer for YamlConverter {

View File

@ -55,6 +55,7 @@ fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
) )
(@subcommand converters => (@subcommand converters =>
(about: "list the available converters") (about: "list the available converters")
(@arg converter: "Converter name to get help for.")
) )
(@subcommand importers => (@subcommand importers =>
(about: "list the available importers for includes") (about: "list the available importers for includes")
@ -252,7 +253,6 @@ fn inspect_command(
builder.set_strict(strict); builder.set_strict(strict);
match registry.get_converter(target) { match registry.get_converter(target) {
Some(converter) => { Some(converter) => {
// TODO(jwall): We should warn if this is a test file.
let result = builder.build(file); let result = builder.build(file);
if !result.is_ok() { if !result.is_ok() {
eprintln!("{:?}", result.err().unwrap()); eprintln!("{:?}", result.err().unwrap());
@ -393,14 +393,32 @@ fn test_command(
process::exit(0); process::exit(0);
} }
fn converters_command(registry: &ConverterRegistry) { fn converters_command(matches: &clap::ArgMatches, registry: &ConverterRegistry) {
println!("Available converters:"); if let Some(ref cname) = matches.value_of("converter") {
println!(""); let mut found = false;
for (name, c) in registry.get_converter_list().iter() { for (name, c) in registry.get_converter_list().iter() {
println!("- {}", name); if cname == name {
println!(" Description: {}", c.description()); println!("* {}", name);
println!(" Output Extension: `.{}`", c.file_ext()); println!("Description: {}", c.description());
println!("Output Extension: `.{}`", c.file_ext());
println!("");
println!("{}", c.help());
found = true;
}
}
if !found {
println!("No such converter {}", cname);
process::exit(1);
}
} else {
println!("Available converters:");
println!(""); println!("");
for (name, c) in registry.get_converter_list().iter() {
println!("* {}", name);
println!("Description: {}", c.description());
println!("Output Extension: `.{}`", c.file_ext());
println!("");
}
} }
} }
@ -457,8 +475,8 @@ fn main() {
build_command(matches, &import_paths, cache, &registry, strict); build_command(matches, &import_paths, cache, &registry, strict);
} else if let Some(matches) = app_matches.subcommand_matches("test") { } else if let Some(matches) = app_matches.subcommand_matches("test") {
test_command(matches, &import_paths, cache, &registry, strict); test_command(matches, &import_paths, cache, &registry, strict);
} else if let Some(_) = app_matches.subcommand_matches("converters") { } else if let Some(matches) = app_matches.subcommand_matches("converters") {
converters_command(&registry) converters_command(matches, &registry)
} else if let Some(_) = app_matches.subcommand_matches("importers") { } else if let Some(_) = app_matches.subcommand_matches("importers") {
let registry = ImporterRegistry::make_registry(); let registry = ImporterRegistry::make_registry();
importers_command(&registry) importers_command(&registry)