1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
|
//! 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<String> = None;
let mut root: Option<PathBuf> = 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<Self, Self::Err> {
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
}
|