//! A very partial unfaithful implementation of cargo's command line. //! //! This showcases some hairier patterns, like subcommands and custom value parsing. use std::{path::PathBuf, str::FromStr}; const HELP: &str = "cargo [+toolchain] [OPTIONS] [SUBCOMMAND]"; fn main() -> Result<(), lexopt::Error> { use lexopt::prelude::*; let mut settings = GlobalSettings { toolchain: "stable".to_owned(), color: Color::Auto, offline: false, quiet: false, verbose: false, }; let mut parser = lexopt::Parser::from_env(); while let Some(arg) = parser.next()? { match arg { Long("color") => { settings.color = parser.value()?.parse()?; } Long("offline") => { settings.offline = true; } Long("quiet") => { settings.quiet = true; settings.verbose = false; } Long("verbose") => { settings.verbose = true; settings.quiet = false; } Long("help") => { println!("{}", HELP); std::process::exit(0); } Value(value) => { let value = value.string()?; match value.as_str() { value if value.starts_with('+') => { settings.toolchain = value[1..].to_owned(); } "install" => { return install(settings, parser); } value => { return Err(format!("unknown subcommand '{}'", value).into()); } } } _ => return Err(arg.unexpected()), } } println!("{}", HELP); Ok(()) } #[derive(Debug)] struct GlobalSettings { toolchain: String, color: Color, offline: bool, quiet: bool, verbose: bool, } fn install(settings: GlobalSettings, mut parser: lexopt::Parser) -> Result<(), lexopt::Error> { use lexopt::prelude::*; let mut package: Option = None; let mut root: Option = None; let mut jobs: u16 = get_no_of_cpus(); while let Some(arg) = parser.next()? { match arg { Value(value) if package.is_none() => { package = Some(value.string()?); } Long("root") => { root = Some(parser.value()?.into()); } Short('j') | Long("jobs") => { jobs = parser.value()?.parse()?; } Long("help") => { println!("cargo install [OPTIONS] CRATE"); std::process::exit(0); } _ => return Err(arg.unexpected()), } } println!("Settings: {:#?}", settings); println!( "Installing {} into {:?} with {} jobs", package.ok_or("missing CRATE argument")?, root, jobs ); Ok(()) } #[derive(Debug)] enum Color { Auto, Always, Never, } // clap has a macro for this: https://docs.rs/clap/2.33.3/clap/macro.arg_enum.html // We have to do it manually. impl FromStr for Color { type Err = String; fn from_str(s: &str) -> Result { match s.to_lowercase().as_str() { "auto" => Ok(Color::Auto), "always" => Ok(Color::Always), "never" => Ok(Color::Never), _ => Err(format!( "Invalid style '{}' [pick from: auto, always, never]", s )), } } } fn get_no_of_cpus() -> u16 { 4 }