mirror of
https://github.com/zaphar/ucg.git
synced 2025-07-22 18:19:54 -04:00
703 lines
23 KiB
Rust
703 lines
23 KiB
Rust
// Copyright 2017 Jeremy Wall <jeremy@marzhillstudios.com>
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
#[macro_use]
|
|
extern crate clap;
|
|
extern crate dirs;
|
|
extern crate rustyline;
|
|
extern crate ucglib;
|
|
|
|
use std::cell::RefCell;
|
|
use std::collections::BTreeMap;
|
|
use std::error::Error;
|
|
use std::fs::File;
|
|
use std::io;
|
|
use std::io::Read;
|
|
use std::path::{Path, PathBuf};
|
|
use std::process;
|
|
use std::rc::Rc;
|
|
|
|
use ucglib::build;
|
|
use ucglib::build::assets::{Cache, MemoryCache};
|
|
use ucglib::build::Val;
|
|
use ucglib::convert::traits;
|
|
use ucglib::convert::{ConverterRegistry, ImporterRegistry};
|
|
use ucglib::iter::OffsetStrIter;
|
|
use ucglib::parse::parse;
|
|
|
|
fn do_flags<'a, 'b>() -> clap::App<'a, 'b> {
|
|
clap_app!(
|
|
ucg =>
|
|
(version: crate_version!())
|
|
(author: crate_authors!())
|
|
(about: "Universal Configuration Grammar compiler.")
|
|
(@arg nostrict: --("no-strict") "Turn off strict checking.")
|
|
(@subcommand eval =>
|
|
(about: "Evaluate an expression with an optional ucg file as context.")
|
|
(@arg expr: --expr -e +takes_value +required "Expression to evaluate.")
|
|
(@arg target: --format +takes_value "Output type. (flags, json, env, exec) defaults to json.")
|
|
(@arg INPUT: "ucg file to use as context for the expression.")
|
|
)
|
|
(@subcommand repl =>
|
|
(about: "Start the ucg repl for interactive evaluation.")
|
|
)
|
|
(@subcommand build =>
|
|
(about: "Build a list of ucg files.")
|
|
(@arg recurse: -r "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 test =>
|
|
(about: "Check a list of ucg files for errors and run test assertions.")
|
|
(@arg recurse: -r "Whether we should recurse or not.")
|
|
(@arg INPUT: ... "Input ucg files or directories to run test assertions for. If not provided it will scan the current directory for files with _test.ucg")
|
|
)
|
|
(@subcommand fmt =>
|
|
(about: "Format ucg files automatically.")
|
|
(@arg recurse: -r "Whether we should recurse or not.")
|
|
(@arg indent: -i --indent "How many spaces to indent by. Defaults to 4")
|
|
(@arg INPUT: ... "Input ucg files or directories to format")
|
|
)
|
|
(@subcommand converters =>
|
|
(about: "list the available converters")
|
|
(@arg converter: "Converter name to get help for.")
|
|
)
|
|
(@subcommand importers =>
|
|
(about: "list the available importers for includes")
|
|
)
|
|
(@subcommand env =>
|
|
(about: "Describe the environment variables ucg uses.")
|
|
)
|
|
)
|
|
}
|
|
|
|
fn run_converter(c: &dyn traits::Converter, v: Rc<Val>, f: Option<&str>) -> traits::ConvertResult {
|
|
let mut file: Box<dyn std::io::Write> = match f {
|
|
Some(f) => {
|
|
let mut path_buf = PathBuf::from(f);
|
|
path_buf.set_extension(c.file_ext());
|
|
let new_path = path_buf.to_str().unwrap();
|
|
Box::new(File::create(&new_path)?)
|
|
}
|
|
None => Box::new(io::stdout()),
|
|
};
|
|
let result = c.convert(v, file.as_mut());
|
|
file.flush()?;
|
|
result
|
|
}
|
|
|
|
fn build_file<'a, C: Cache>(
|
|
file: &'a str,
|
|
validate: bool,
|
|
strict: bool,
|
|
import_paths: &'a Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &'a ConverterRegistry,
|
|
) -> Result<build::FileBuilder<'a, C>, Box<dyn Error>> {
|
|
let mut file_path_buf = PathBuf::from(file);
|
|
if file_path_buf.is_relative() {
|
|
file_path_buf = std::env::current_dir()?.join(file_path_buf);
|
|
}
|
|
let mut builder =
|
|
build::FileBuilder::new(std::env::current_dir()?, import_paths, cache, registry);
|
|
builder.set_strict(strict);
|
|
if validate {
|
|
builder.enable_validate_mode();
|
|
}
|
|
builder.build(file_path_buf)?;
|
|
if validate {
|
|
println!("{}", builder.assert_collector.summary);
|
|
}
|
|
Ok(builder)
|
|
}
|
|
|
|
fn do_validate<C: Cache>(
|
|
file: &str,
|
|
strict: bool,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
) -> bool {
|
|
println!("Validating {}", file);
|
|
match build_file(file, true, strict, import_paths, cache, registry) {
|
|
Ok(b) => {
|
|
if b.assert_collector.success {
|
|
println!("File {} Pass\n", file);
|
|
} else {
|
|
println!("File {} Fail\n", file);
|
|
return false;
|
|
}
|
|
}
|
|
Err(msg) => {
|
|
eprintln!("Err: {}", msg);
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
fn process_output(
|
|
output: &Option<(String, Rc<Val>)>,
|
|
file: Option<&str>,
|
|
registry: &ConverterRegistry,
|
|
) -> bool {
|
|
let (typ, val) = match output {
|
|
Some((ref typ, ref val)) => (typ, val.clone()),
|
|
None => {
|
|
return false;
|
|
}
|
|
};
|
|
match registry.get_converter(typ) {
|
|
Some(converter) => {
|
|
run_converter(converter, val, file).unwrap();
|
|
eprintln!("\nConversion successful");
|
|
return true;
|
|
}
|
|
None => {
|
|
eprintln!("No such converter {}", typ);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
fn do_compile<C: Cache>(
|
|
file: &str,
|
|
strict: bool,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
) -> bool {
|
|
println!("Building {}", file);
|
|
let builder = match build_file(file, false, strict, import_paths, cache.clone(), registry) {
|
|
Ok(builder) => builder,
|
|
Err(err) => {
|
|
eprintln!("{}", err);
|
|
return false;
|
|
}
|
|
};
|
|
if builder.out_lock.is_none() {
|
|
eprintln!("Build results in no artifacts.");
|
|
}
|
|
process_output(&builder.out_lock, Some(file), registry)
|
|
}
|
|
|
|
fn visit_ucg_files<C: Cache>(
|
|
path: &Path,
|
|
recurse: bool,
|
|
validate: bool,
|
|
strict: bool,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
) -> Result<bool, Box<dyn Error>> {
|
|
let our_path = String::from(path.to_string_lossy());
|
|
let mut result = true;
|
|
let mut summary = String::new();
|
|
if path.is_dir() {
|
|
let mut dir_iter = std::fs::read_dir(path)?.peekable();
|
|
loop {
|
|
let entry = match dir_iter.next() {
|
|
Some(e) => e,
|
|
None => {
|
|
break;
|
|
}
|
|
};
|
|
let next_item = 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(e) = visit_ucg_files(
|
|
&next_path,
|
|
recurse,
|
|
validate,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
registry,
|
|
) {
|
|
eprintln!("{}", e);
|
|
result = false;
|
|
}
|
|
} else {
|
|
if validate && path_as_string.ends_with("_test.ucg") {
|
|
if !do_validate(
|
|
&path_as_string,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
registry,
|
|
) {
|
|
result = false;
|
|
summary.push_str(format!("{} - FAIL\n", path_as_string).as_str())
|
|
} else {
|
|
summary.push_str(format!("{} - PASS\n", path_as_string).as_str())
|
|
}
|
|
} else if !validate && path_as_string.ends_with(".ucg") {
|
|
if !do_compile(
|
|
&path_as_string,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
registry,
|
|
) {
|
|
result = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else if validate && our_path.ends_with("_test.ucg") {
|
|
if !do_validate(&our_path, strict, import_paths, cache, registry) {
|
|
result = false;
|
|
summary.push_str(format!("{} - FAIL\n", our_path).as_str());
|
|
} else {
|
|
summary.push_str(format!("{} - PASS\n", &our_path).as_str());
|
|
}
|
|
} else if !validate {
|
|
if !do_compile(&our_path, strict, import_paths, cache, registry) {
|
|
result = false;
|
|
}
|
|
}
|
|
if validate && !summary.is_empty() {
|
|
println!("RESULTS:");
|
|
println!("{}", summary);
|
|
}
|
|
Ok(result)
|
|
}
|
|
|
|
fn inspect_command<C: Cache>(
|
|
matches: &clap::ArgMatches,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
strict: bool,
|
|
) {
|
|
let file = matches.value_of("INPUT");
|
|
let sym = matches.value_of("expr");
|
|
let target = matches.value_of("target").unwrap_or("json");
|
|
let mut builder = build::FileBuilder::new(
|
|
std::env::current_dir().unwrap(),
|
|
import_paths,
|
|
cache,
|
|
registry,
|
|
);
|
|
builder.set_strict(strict);
|
|
match registry.get_converter(target) {
|
|
Some(converter) => {
|
|
if let Some(file) = file {
|
|
if let Err(e) = builder.build(file) {
|
|
eprintln!("{:?}", e);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
let val = match sym {
|
|
Some(sym_name) => {
|
|
let normalized = if !sym_name.ends_with(";") {
|
|
let mut temp = sym_name.to_owned();
|
|
temp.push_str(";");
|
|
temp
|
|
} else {
|
|
sym_name.to_owned()
|
|
};
|
|
let mut builder = builder.clone_builder();
|
|
match builder.eval_string(&normalized) {
|
|
Ok(v) => Some(v.clone()),
|
|
Err(e) => {
|
|
eprintln!("{}", e);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
None => builder.last,
|
|
};
|
|
match val {
|
|
Some(value) => {
|
|
// We use None here because we always output to stdout for an inspect.
|
|
run_converter(converter, value, None).unwrap();
|
|
println!("");
|
|
process::exit(0);
|
|
}
|
|
None => {
|
|
eprintln!("No value.");
|
|
process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
None => {
|
|
eprintln!(
|
|
"No such format {}\nrun `ucg converters` to see available formats.",
|
|
target
|
|
);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn build_command<C: Cache>(
|
|
matches: &clap::ArgMatches,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
strict: bool,
|
|
) {
|
|
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,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
®istry,
|
|
);
|
|
if let Ok(false) = ok {
|
|
process::exit(1)
|
|
}
|
|
process::exit(0);
|
|
}
|
|
for file in files.unwrap() {
|
|
let pb = PathBuf::from(file);
|
|
if let Ok(false) = visit_ucg_files(
|
|
&pb,
|
|
recurse,
|
|
false,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
®istry,
|
|
) {
|
|
ok = false;
|
|
}
|
|
}
|
|
if !ok {
|
|
process::exit(1)
|
|
}
|
|
}
|
|
|
|
fn fmt_file(p: &Path, indent: usize) -> std::result::Result<(), Box<dyn Error>> {
|
|
let mut f = File::open(p)?;
|
|
let mut contents = String::new();
|
|
f.read_to_string(&mut contents)?;
|
|
let mut comment_map = BTreeMap::new();
|
|
let stmts = parse(OffsetStrIter::new(&contents), Some(&mut comment_map))?;
|
|
let mut printer = ucglib::ast::printer::AstPrinter::new(indent, std::io::stdout())
|
|
.with_comment_map(&comment_map);
|
|
printer.render(&stmts)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn fmt_dir(p: &Path, recurse: bool, indent: usize) -> std::result::Result<(), Box<dyn Error>> {
|
|
// TODO(jwall): We should handle this error more gracefully
|
|
// for the user here.
|
|
let dir_iter = std::fs::read_dir(p)?.peekable();
|
|
for entry in dir_iter {
|
|
let next_item = entry.unwrap();
|
|
let path = next_item.path();
|
|
if path.is_dir() && recurse {
|
|
fmt_dir(&path, recurse, indent)?;
|
|
} else {
|
|
fmt_file(&path, indent)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn fmt_command(matches: &clap::ArgMatches) -> std::result::Result<(), Box<dyn Error>> {
|
|
let files = matches.values_of("INPUT");
|
|
let recurse = matches.is_present("recurse");
|
|
let indent = match matches.value_of("indent") {
|
|
Some(s) => s.parse::<usize>()?,
|
|
None => 4,
|
|
};
|
|
|
|
let mut paths = Vec::new();
|
|
if files.is_none() {
|
|
paths.push(std::env::current_dir()?);
|
|
} else {
|
|
for f in files.unwrap() {
|
|
paths.push(PathBuf::from(f));
|
|
}
|
|
}
|
|
for p in paths {
|
|
if p.is_dir() {
|
|
fmt_dir(&p, recurse, indent)?;
|
|
} else {
|
|
fmt_file(&p, indent)?;
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
fn test_command<C: Cache>(
|
|
matches: &clap::ArgMatches,
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
strict: bool,
|
|
) {
|
|
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,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
®istry,
|
|
);
|
|
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,
|
|
strict,
|
|
import_paths,
|
|
cache.clone(),
|
|
®istry,
|
|
) {
|
|
ok = false;
|
|
}
|
|
}
|
|
if !ok {
|
|
process::exit(1)
|
|
}
|
|
}
|
|
process::exit(0);
|
|
}
|
|
|
|
fn converters_command(matches: &clap::ArgMatches, registry: &ConverterRegistry) {
|
|
if let Some(ref cname) = matches.value_of("converter") {
|
|
let mut found = false;
|
|
for (name, c) in registry.get_converter_list().iter() {
|
|
if cname == name {
|
|
println!("* {}", name);
|
|
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!("");
|
|
for (name, c) in registry.get_converter_list().iter() {
|
|
println!("* {}", name);
|
|
println!("Description: {}", c.description());
|
|
println!("Output Extension: `.{}`", c.file_ext());
|
|
println!("");
|
|
}
|
|
}
|
|
}
|
|
|
|
fn importers_command(registry: &ImporterRegistry) {
|
|
println!("Available importers");
|
|
println!("");
|
|
for (name, _importer) in registry.get_importer_list().iter() {
|
|
println!("- {}", name);
|
|
}
|
|
}
|
|
|
|
fn env_help() {
|
|
println!(
|
|
include_str!("help/env.txt"),
|
|
std::env::var("UCG_IMPORT_PATH").unwrap_or(String::new())
|
|
);
|
|
}
|
|
|
|
fn print_repl_help() {
|
|
println!(include_str!("help/repl.txt"));
|
|
}
|
|
|
|
fn do_repl<C: Cache>(
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
) -> std::result::Result<(), Box<dyn Error>> {
|
|
let config = rustyline::Config::builder();
|
|
let mut editor = rustyline::Editor::<()>::with_config(
|
|
config
|
|
.history_ignore_space(true)
|
|
.history_ignore_dups(false)
|
|
.build(),
|
|
);
|
|
let path_home = dirs::home_dir().unwrap_or(std::env::temp_dir());
|
|
let config_home = std::env::var("XDG_CACHE_HOME")
|
|
.unwrap_or_else(|_| format!("{}/.cache", path_home.to_string_lossy()));
|
|
let mut config_home = PathBuf::from(config_home);
|
|
config_home.push("ucg");
|
|
config_home.push("line_hist");
|
|
if editor.load_history(&config_home).is_err() {
|
|
eprintln!(
|
|
"No history file {} Continuing without history.",
|
|
config_home.to_string_lossy()
|
|
);
|
|
// introduce a scope so the file will get automatically closed after
|
|
{
|
|
let base_dir = config_home.parent().unwrap();
|
|
if !base_dir.exists() {
|
|
if let Err(e) = std::fs::create_dir_all(base_dir) {
|
|
eprintln!("{}", e);
|
|
}
|
|
}
|
|
if let Err(e) = std::fs::File::create(&config_home) {
|
|
eprintln!("{}", e);
|
|
}
|
|
}
|
|
}
|
|
let mut builder =
|
|
build::FileBuilder::new(std::env::current_dir()?, import_paths, cache, registry);
|
|
// loop
|
|
let mut lines = ucglib::io::StatementAccumulator::new();
|
|
println!("Welcome to the UCG repl. Ctrl-D to exit");
|
|
println!("Type '#help' for help.");
|
|
println!("");
|
|
loop {
|
|
// print prompt
|
|
let line = editor.readline(&format!("{}> ", lines.next_line()))?;
|
|
// TODO check for a repl command.
|
|
// repl commands are only valid while not accumulating a statement;
|
|
let trimmed = line.trim();
|
|
if trimmed.starts_with("#") {
|
|
// handle the various commands.
|
|
if trimmed.starts_with("#help") {
|
|
print_repl_help();
|
|
} else if trimmed.starts_with("#del") {
|
|
// remove a named binding from the builder output.
|
|
let args: Vec<&str> = trimmed.split(" ").skip(1).collect();
|
|
if args.len() != 1 {
|
|
// print usage of the #del command
|
|
eprintln!("The '#del' command expects a single argument specifying \nthe binding to delete.");
|
|
} else {
|
|
let key = ucglib::ast::PositionedItem {
|
|
pos: ucglib::ast::Position::new(0, 0, 0),
|
|
val: args[0].to_string(),
|
|
};
|
|
if let None = builder.scope_mut().build_output.remove(&key) {
|
|
eprintln!("No such binding {}", key.val);
|
|
}
|
|
}
|
|
} else {
|
|
eprintln!("Invalid repl command...");
|
|
eprintln!("");
|
|
print_repl_help();
|
|
}
|
|
continue;
|
|
}
|
|
lines.push(line);
|
|
// check to see if that line is a statement
|
|
loop {
|
|
// read a statement
|
|
if let Some(stmt) = lines.get_statement() {
|
|
// if it is then
|
|
// eval statement
|
|
match builder.eval_string(&stmt) {
|
|
// print the result
|
|
Err(e) => eprintln!("{}", e),
|
|
Ok(v) => {
|
|
if builder.out_lock.is_some() {
|
|
process_output(&builder.out_lock, None, registry);
|
|
builder.out_lock = None;
|
|
} else {
|
|
println!("{}", v);
|
|
editor.history_mut().add(stmt);
|
|
editor.save_history(&config_home)?;
|
|
}
|
|
}
|
|
}
|
|
// start loop over at prompt.
|
|
break;
|
|
}
|
|
// if not then keep accumulating lines without a prompt
|
|
lines.push(editor.readline(&format!("{}> ", lines.next_line()))?);
|
|
}
|
|
}
|
|
}
|
|
|
|
fn repl<C: Cache>(
|
|
import_paths: &Vec<PathBuf>,
|
|
cache: Rc<RefCell<C>>,
|
|
registry: &ConverterRegistry,
|
|
) {
|
|
if let Err(e) = do_repl(import_paths, cache, registry) {
|
|
eprintln!("{}", e);
|
|
process::exit(1);
|
|
}
|
|
}
|
|
|
|
fn main() {
|
|
let mut app = do_flags();
|
|
let app_matches = app.clone().get_matches();
|
|
let cache = Rc::new(RefCell::new(MemoryCache::new()));
|
|
let registry = ConverterRegistry::make_registry();
|
|
let mut import_paths = Vec::new();
|
|
if let Some(mut p) = dirs::home_dir() {
|
|
p.push(".ucg");
|
|
// Attempt to create directory if it doesn't exist.
|
|
if !p.exists() {
|
|
std::fs::create_dir(&p).unwrap();
|
|
}
|
|
import_paths.push(p);
|
|
}
|
|
if let Ok(path_list_str) = std::env::var("UCG_IMPORT_PATH") {
|
|
for p in std::env::split_paths(&path_list_str) {
|
|
import_paths.push(p);
|
|
}
|
|
}
|
|
let strict = if app_matches.is_present("nostrict") {
|
|
false
|
|
} else {
|
|
true
|
|
};
|
|
if let Some(matches) = app_matches.subcommand_matches("eval") {
|
|
inspect_command(matches, &import_paths, cache, ®istry, strict);
|
|
} else if let Some(matches) = app_matches.subcommand_matches("build") {
|
|
build_command(matches, &import_paths, cache, ®istry, strict);
|
|
} else if let Some(matches) = app_matches.subcommand_matches("test") {
|
|
test_command(matches, &import_paths, cache, ®istry, strict);
|
|
} else if let Some(matches) = app_matches.subcommand_matches("converters") {
|
|
converters_command(matches, ®istry)
|
|
} else if let Some(_) = app_matches.subcommand_matches("importers") {
|
|
let registry = ImporterRegistry::make_registry();
|
|
importers_command(®istry)
|
|
} else if let Some(_) = app_matches.subcommand_matches("env") {
|
|
env_help()
|
|
} else if let Some(_) = app_matches.subcommand_matches("repl") {
|
|
repl(&import_paths, cache, ®istry)
|
|
} else if let Some(matches) = app_matches.subcommand_matches("fmt") {
|
|
if let Err(e) = fmt_command(matches) {
|
|
eprintln!("{}", e);
|
|
process::exit(1);
|
|
}
|
|
} else {
|
|
app.print_help().unwrap();
|
|
println!("");
|
|
}
|
|
}
|