use std::ops::Deref; use std::path::PathBuf; use clap::{CommandFactory, Parser, Subcommand}; pub struct Args { inner: ArgsInner, } #[derive(Parser)] pub struct ArgsInner { #[clap(subcommand)] pub command: Command, } #[derive(Subcommand)] pub enum Command { Lex { input: PathBuf, output_tokens: PathBuf, output_symbols: PathBuf, }, Syn { input: PathBuf, output_tree: PathBuf, }, Sem { input: PathBuf, output_tree: PathBuf, }, Gen { mode: GenMode, #[clap(short, long, default_value_t = false)] optimize: bool, input: PathBuf, output: PathBuf, output_symbols: PathBuf, }, Com { #[clap(short, long, default_value_t = false)] optimize: bool, input: PathBuf, output: PathBuf, }, Int { input: PathBuf, }, } #[derive(Copy, Clone, PartialEq, Eq, clap::ValueEnum)] pub enum GenMode { #[clap(name = "intermediate", alias = "i")] Intermediate, #[clap(name = "postfix", alias = "p")] Postfix, } impl Args { pub fn parse() -> Self { let inner = match validate_inner(ArgsInner::parse()) { Ok(args) => args, Err(err) => { let mut command = ArgsInner::command(); err.format(&mut command).exit(); }, }; Self { inner } } } impl Deref for Args { type Target = ArgsInner; fn deref(&self) -> &Self::Target { &self.inner } } fn validate_inner(args: ArgsInner) -> Result { match &args.command { Command::Lex { input, output_tokens, output_symbols, } => validate_lex(input, output_tokens, output_symbols)?, Command::Syn { input, output_tree } => validate_syn(input, output_tree)?, Command::Sem { input, output_tree } => validate_sem(input, output_tree)?, Command::Gen { input, output, output_symbols, .. } => validate_gen(input, output, output_symbols)?, Command::Com { input, output, .. } => validate_com(input, output)?, Command::Int { input } => validate_int(input)?, }; Ok(args) } fn validate_lex( input: &PathBuf, output_tokens: &PathBuf, output_symbols: &PathBuf, ) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } if input == output_tokens { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); } if input == output_symbols { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; Ok(()) } fn validate_syn(input: &PathBuf, output_tree: &PathBuf) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } if output_tree == input { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; Ok(()) } fn validate_sem(input: &PathBuf, output_tree: &PathBuf) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } if output_tree == input { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; Ok(()) } fn validate_gen( input: &PathBuf, output: &PathBuf, output_symbols: &PathBuf, ) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } if input == output { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; if input == output_symbols { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; Ok(()) } fn validate_com(input: &PathBuf, output: &PathBuf) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } if input == output { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, "Input and output files cannot be the same", )); }; Ok(()) } fn validate_int(input: &PathBuf) -> Result<(), clap::Error> { if !input.is_file() { return Err(clap::Error::raw( clap::error::ErrorKind::InvalidValue, format!("Input file '{}' does not exist", input.display()), )); } Ok(()) }