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:
parent
b49eb814c3
commit
a026e39d90
130
src/main.rs
130
src/main.rs
@ -16,16 +16,130 @@ mod transformations;
|
||||
mod typecheck;
|
||||
mod types;
|
||||
|
||||
use crate::error::{Error, IOError, SerializationError};
|
||||
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;
|
||||
|
||||
fn main() {
|
||||
match Program::new_from_stdin() {
|
||||
Ok(mut p) => match p.eval() {
|
||||
Ok(t) => println!("Done: {:?}", t),
|
||||
Err(err) => p.report(err),
|
||||
},
|
||||
Err(msg) => eprintln!("Error when reading the source: {}", msg),
|
||||
};
|
||||
/// Command-line options and subcommands.
|
||||
#[derive(StructOpt, Debug)]
|
||||
/// The interpreter of the Nickel language.
|
||||
struct Opt {
|
||||
/// The input file. Standard input by default
|
||||
#[structopt(short = "f", long)]
|
||||
#[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)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user