1
1
mirror of https://github.com/tweag/nickel.git synced 2024-09-20 16:08:14 +03:00

Add subcommands and options to the executable

This commit is contained in:
Yann Hamdaoui 2020-11-17 14:41:53 +01:00
parent b49eb814c3
commit a026e39d90

View File

@ -16,16 +16,130 @@ mod transformations;
mod typecheck; mod typecheck;
mod types; mod types;
use crate::error::{Error, IOError, SerializationError};
use crate::program::Program; use crate::program::Program;
use crate::term::RichTerm;
use std::io::prelude::Write;
use std::path::PathBuf;
use std::str::FromStr;
use std::{fmt, fs, io, process};
// use std::ffi::OsStr;
use structopt::StructOpt;
extern crate either; extern crate either;
fn main() { /// Command-line options and subcommands.
match Program::new_from_stdin() { #[derive(StructOpt, Debug)]
Ok(mut p) => match p.eval() { /// The interpreter of the Nickel language.
Ok(t) => println!("Done: {:?}", t), struct Opt {
Err(err) => p.report(err), /// The input file. Standard input by default
}, #[structopt(short = "f", long)]
Err(msg) => eprintln!("Error when reading the source: {}", msg), #[structopt(parse(from_os_str))]
}; file: Option<PathBuf>,
#[structopt(subcommand)]
command: Option<Command>,
}
/// Available export formats.
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum ExportFormat {
Json,
}
impl std::default::Default for ExportFormat {
fn default() -> Self {
ExportFormat::Json
}
}
impl fmt::Display for ExportFormat {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "json")
}
}
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct ParseFormatError(String);
impl fmt::Display for ParseFormatError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "unsupported export format {}", self.0)
}
}
impl FromStr for ExportFormat {
type Err = ParseFormatError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s.to_lowercase().as_ref() {
"json" => Ok(ExportFormat::Json),
_ => Err(ParseFormatError(String::from(s))),
}
}
}
/// Available subcommands.
#[derive(StructOpt, Debug)]
enum Command {
/// Export the result to a different format
Export {
/// Available formats: `json`. Default format: `json`.
#[structopt(long)]
format: Option<ExportFormat>,
/// Output file. Standard output by default
#[structopt(short = "o", long)]
#[structopt(parse(from_os_str))]
output: Option<PathBuf>,
},
/// Typecheck a program, but do not run it
Typecheck,
}
fn main() {
let opts = Opt::from_args();
let mut program = opts
.file
.map(|path: PathBuf| -> io::Result<_> {
let file = fs::File::open(&path)?;
Program::new_from_source(file, &path)
})
.unwrap_or_else(Program::new_from_stdin)
.unwrap_or_else(|err| {
eprintln!("Error when reading input: {}", err);
process::exit(1)
});
let result = match opts.command {
Some(Command::Export { format, output }) => {
program.eval_full().map(RichTerm::from).and_then(|rt| {
serialize::validate(&rt).map_err(Error::from)?;
let data = match format.unwrap_or_default() {
ExportFormat::Json => serde_json::to_string_pretty(&rt),
}
.map_err(|err| SerializationError::Other(err.to_string()))?;
if let Some(file) = output {
let mut file = fs::File::create(&file).map_err(IOError::from)?;
file.write_all(data.as_bytes()).map_err(IOError::from)?;
} else {
io::stdout()
.write_all(data.as_bytes())
.map_err(IOError::from)?;
}
Ok(())
})
}
Some(Command::Typecheck) => program.typecheck().map(|ty| println!("Ok: {}", ty)),
None => program.eval().and_then(|t| {
println!("Done: {:?}", t);
Ok(())
}),
};
if let Err(err) = result {
program.report(err);
process::exit(1)
}
} }