[DEV-7147] Add "tamec" executable
Merge branch 'jira-7147' * jira-7147: [DEV-7147] Build "xmli" files using "tamec" [DEV-7147] Add "tamec" executablemaster
commit
3dabc126f2
|
@ -115,7 +115,7 @@ program-ui: ui/package.strip.js ui/Program.js ui/html/index.phtml
|
|||
|
||||
# Handle an intermediate step as we transition to the new compiler
|
||||
%.xmli: %.xml
|
||||
cp $< $@
|
||||
$(path_tame)/tamer/target/release/tamec --emit xmlo -o $@ $<
|
||||
|
||||
# Individual dependencies appear in suppliers.mk (see below)
|
||||
%.xmlo: %.xmli $(path_tame)/.rev-xmlo
|
||||
|
|
|
@ -0,0 +1,317 @@
|
|||
// TAME compiler
|
||||
//
|
||||
// Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
//! This is the TAME compiler. Its job is to take each of the source code
|
||||
//! files and produce a compiled file.
|
||||
|
||||
extern crate tamer;
|
||||
|
||||
use getopts::{Fail, Options};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
use std::ffi::OsStr;
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
/// Types of commands
|
||||
enum Command {
|
||||
Compile(String, String, String),
|
||||
Usage,
|
||||
}
|
||||
|
||||
/// Entrypoint for the compiler
|
||||
pub fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let program = &args[0];
|
||||
let opts = get_opts();
|
||||
let usage = opts.usage(&format!("Usage: {} [OPTIONS] INPUT", program));
|
||||
|
||||
match parse_options(opts, args) {
|
||||
Ok(Command::Compile(input, _, output)) => {
|
||||
let source = Path::new(&input);
|
||||
if source.extension() != Some(OsStr::new("xml")) {
|
||||
panic!("{}: file format not recognized", input);
|
||||
}
|
||||
|
||||
let dest = Path::new(&output);
|
||||
fs::copy(source, dest)?;
|
||||
Ok(())
|
||||
}
|
||||
Ok(Command::Usage) => {
|
||||
println!("{}", usage);
|
||||
std::process::exit(exitcode::OK);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("{}", e);
|
||||
println!("{}", usage);
|
||||
std::process::exit(exitcode::USAGE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get 'Options'
|
||||
///
|
||||
/// ```
|
||||
/// use getopts::Options;
|
||||
///
|
||||
/// let opts = get_opts();
|
||||
/// ```
|
||||
fn get_opts() -> Options {
|
||||
let mut opts = Options::new();
|
||||
opts.optopt("o", "output", "set output file name", "NAME");
|
||||
opts.optopt("", "emit", "set output type", "xmlo");
|
||||
opts.optflag("h", "help", "print this help menu");
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
/// Option parser
|
||||
fn parse_options(opts: Options, args: Vec<String>) -> Result<Command, Fail> {
|
||||
let matches = match opts.parse(&args[1..]) {
|
||||
Ok(m) => m,
|
||||
Err(f) => {
|
||||
return Err(f);
|
||||
}
|
||||
};
|
||||
|
||||
if matches.opt_present("h") {
|
||||
return Ok(Command::Usage);
|
||||
}
|
||||
|
||||
let input = match matches.free.len() {
|
||||
0 => return Err(Fail::OptionMissing(String::from("INPUT"))),
|
||||
1 => matches.free[0].clone(),
|
||||
_ => return Err(Fail::UnrecognizedOption(matches.free[1].clone())),
|
||||
};
|
||||
|
||||
let emit = match matches.opt_str("emit") {
|
||||
Some(m) => match &m[..] {
|
||||
"xmlo" => m,
|
||||
_ => {
|
||||
return Err(Fail::ArgumentMissing(String::from("--emit xmlo")))
|
||||
}
|
||||
},
|
||||
None => {
|
||||
return Err(Fail::OptionMissing(String::from("--emit xmlo")));
|
||||
}
|
||||
};
|
||||
|
||||
let output = match matches.opt_str("o") {
|
||||
Some(m) => m,
|
||||
// we check that the extension is "xml" later
|
||||
None => format!("{}o", input),
|
||||
};
|
||||
|
||||
Ok(Command::Compile(input, emit, output))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_options_help() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![String::from("program"), String::from("-h")],
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(Command::Usage) => {}
|
||||
_ => panic!("Long help option did not parse"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_help_long() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![String::from("program"), String::from("--help")],
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(Command::Usage) => {}
|
||||
_ => panic!("Help option did not parse"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_invalid() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![String::from("program"), String::from("-q")],
|
||||
);
|
||||
|
||||
match result {
|
||||
Err(Fail::UnrecognizedOption(_)) => {}
|
||||
_ => panic!("Invalid option not caught"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_missing_input() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(opts, vec![String::from("program")]);
|
||||
|
||||
match result {
|
||||
Err(Fail::OptionMissing(message)) => {
|
||||
assert_eq!("INPUT", message);
|
||||
}
|
||||
_ => panic!("Missing input not caught"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_missing_emit() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![String::from("program"), String::from("filename")],
|
||||
);
|
||||
|
||||
match result {
|
||||
Err(Fail::OptionMissing(message)) => {
|
||||
assert_eq!("--emit xmlo", message);
|
||||
}
|
||||
_ => panic!("Missing emit not caught"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_invalid_emit() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![
|
||||
String::from("program"),
|
||||
String::from("filename.xml"),
|
||||
String::from("--emit"),
|
||||
String::from("foo"),
|
||||
],
|
||||
);
|
||||
|
||||
match result {
|
||||
Err(Fail::ArgumentMissing(message)) => {
|
||||
assert_eq!("--emit xmlo", message);
|
||||
}
|
||||
_ => panic!("Invalid emit not caught"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_too_many_args() {
|
||||
let opts = get_opts();
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![
|
||||
String::from("program"),
|
||||
String::from("foo"),
|
||||
String::from("--emit"),
|
||||
String::from("bar"),
|
||||
String::from("baz"),
|
||||
],
|
||||
);
|
||||
|
||||
match result {
|
||||
Err(Fail::UnrecognizedOption(message)) => {
|
||||
assert_eq!("baz", message);
|
||||
}
|
||||
_ => panic!("Extra option not caught"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_valid() {
|
||||
let opts = get_opts();
|
||||
let xmlo = String::from("xmlo");
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![
|
||||
String::from("program"),
|
||||
String::from("foo.xml"),
|
||||
String::from("--emit"),
|
||||
xmlo,
|
||||
],
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(Command::Compile(infile, xmlo, outfile)) => {
|
||||
assert_eq!("foo.xml", infile);
|
||||
assert_eq!("foo.xmlo", outfile);
|
||||
assert_eq!("xmlo", xmlo);
|
||||
}
|
||||
_ => panic!("Unexpected result"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_valid_custom_out() {
|
||||
let opts = get_opts();
|
||||
let xmlo = String::from("xmlo");
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![
|
||||
String::from("program"),
|
||||
String::from("foo.xml"),
|
||||
String::from("--emit"),
|
||||
xmlo,
|
||||
String::from("-o"),
|
||||
String::from("foo.xmli"),
|
||||
],
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(Command::Compile(infile, xmlo, outfile)) => {
|
||||
assert_eq!("foo.xml", infile);
|
||||
assert_eq!("foo.xmli", outfile);
|
||||
assert_eq!("xmlo", xmlo);
|
||||
}
|
||||
_ => panic!("Unexpected result"),
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_options_valid_custom_out_long() {
|
||||
let opts = get_opts();
|
||||
let xmlo = String::from("xmlo");
|
||||
let result = parse_options(
|
||||
opts,
|
||||
vec![
|
||||
String::from("program"),
|
||||
String::from("foo.xml"),
|
||||
String::from("--emit"),
|
||||
xmlo,
|
||||
String::from("--output"),
|
||||
String::from("foo.xmli"),
|
||||
],
|
||||
);
|
||||
|
||||
match result {
|
||||
Ok(Command::Compile(infile, xmlo, outfile)) => {
|
||||
assert_eq!("foo.xml", infile);
|
||||
assert_eq!("foo.xmli", outfile);
|
||||
assert_eq!("xmlo", xmlo);
|
||||
}
|
||||
_ => panic!("Unexpected result"),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
// Copyright (C) 2014-2020 Ryan Specialty Group, LLC.
|
||||
//
|
||||
// This file is part of TAME.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use assert_cmd::prelude::*;
|
||||
use predicates::prelude::*;
|
||||
use std::process::Command;
|
||||
|
||||
#[test]
|
||||
fn compile_invalid_argument() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("-q");
|
||||
cmd.assert()
|
||||
.failure()
|
||||
.code(exitcode::USAGE)
|
||||
.stderr(predicate::str::contains("Unrecognized option:"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_missing_input_file() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("--emit").arg("xmlo");
|
||||
cmd.assert()
|
||||
.failure()
|
||||
.code(exitcode::USAGE)
|
||||
.stderr(predicate::str::contains("INPUT"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_missing_emit() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("foobar");
|
||||
cmd.assert()
|
||||
.failure()
|
||||
.code(exitcode::USAGE)
|
||||
.stderr(predicate::str::contains("--emit xmlo"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_invalid_emit() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("foobar");
|
||||
cmd.arg("--emit").arg("foo");
|
||||
cmd.assert()
|
||||
.failure()
|
||||
.code(exitcode::USAGE)
|
||||
.stderr(predicate::str::contains("--emit xmlo"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_input_file_does_not_exist() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("foobar.xml");
|
||||
cmd.arg("--emit").arg("xmlo");
|
||||
cmd.assert()
|
||||
.failure()
|
||||
.code(1)
|
||||
.stderr(predicate::str::contains("No such file or directory"));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn compile_input_file_wrong_extension() -> Result<(), Box<dyn std::error::Error>>
|
||||
{
|
||||
let mut cmd = Command::cargo_bin("tamec")?;
|
||||
cmd.arg("foobar.php");
|
||||
cmd.arg("--emit").arg("xmlo");
|
||||
cmd.assert().failure().stderr(predicate::str::contains(
|
||||
"foobar.php: file format not recognized",
|
||||
));
|
||||
|
||||
Ok(())
|
||||
}
|
Loading…
Reference in New Issue