mul/src/cli.rs

72 lines
2.0 KiB
Rust

//! Command line interface for the language binary.
use std::ffi::OsString;
use std::path::PathBuf;
use pico_args::Arguments;
use thiserror::Error;
/// Usage help text.
pub const USAGE: &str = "
USAGE:
mul [-h|--help] (parse <FILE> | evaluate <FILE> | repl)
FLAGS:
-h, --help Prints help information (this text)
COMMANDS:
parse <FILE> Parse the given file and print a serialized AST to stdout
evaluate, eval <FILE> Evaluate the given file
repl Start the Read-Evaluate-Print-Loop
";
/// Errors that may occur during command line parsing.
#[derive(Debug, Error)]
pub enum Error {
#[error("Problem while parsing arguments, {0}")]
ArgsError(#[from] pico_args::Error),
#[error("Invalid command '{0}'\n{USAGE}")]
InvalidCommand(String),
#[error("Unrecognized arguments {0:?}\n{USAGE}")]
UnrecognizedArguments(Vec<OsString>),
}
/// Possible top level commands.
pub enum Command {
Parse { path: PathBuf },
Run { path: PathBuf },
Repl,
}
impl Command {
/// Parse the command line from the current environment.
pub fn from_environment() -> Result<Option<Self>, Error> {
Self::from_arguments(Arguments::from_env())
}
/// Parse a command from raw arguments.
pub fn from_arguments(mut args: Arguments) -> Result<Option<Self>, Error> {
if args.contains(["-h", "--help"]) {
return Ok(None);
}
let cmd = match args.subcommand()?.as_deref() {
Some("parse") => {
let path = args.free_from_str()?;
Command::Parse { path }
}
Some("run") => {
let path = args.free_from_str()?;
Command::Run { path }
}
Some("repl") | None => Command::Repl,
Some(cmd) => return Err(Error::InvalidCommand(cmd.to_string())),
};
let remaining = args.finish();
if remaining.is_empty() {
Ok(Some(cmd))
} else {
Err(Error::UnrecognizedArguments(remaining))
}
}
}