float support and simple help

This commit is contained in:
nora 2021-10-02 12:55:25 +02:00
parent 3fb8953ff4
commit 37ba39863a
6 changed files with 56 additions and 15 deletions

View file

@ -36,6 +36,7 @@ The following return types are currently available:
* bool * bool
* isize * isize
* usize * usize
* f64
Boolean values can only be `None` or `Some`. Boolean values can only be `None` or `Some`.
The other values can be `None` or `Some(_)` The other values can be `None` or `Some(_)`

View file

@ -37,10 +37,10 @@ mod schema;
use crate::parse::CliArgs; use crate::parse::CliArgs;
use crate::schema::{IntoSchema, Schema, SchemaKind}; use crate::schema::{IntoSchema, Schema, SchemaKind};
use std::any::Any;
pub use error::SchemaError; pub use error::SchemaError;
pub use macros::*; pub use macros::*;
use std::any::Any;
pub type Result<T> = std::result::Result<T, SchemaError>; pub type Result<T> = std::result::Result<T, SchemaError>;
@ -60,7 +60,7 @@ where
let args = CliArgs::from_args(&arg_schema, std::env::args_os()); let args = CliArgs::from_args(&arg_schema, std::env::args_os());
match args { match args {
Ok(args) => BadArgs { args }, Ok(args) => BadArgs { args },
Err(err) => reporting::report(err), Err(err) => reporting::report(err, &arg_schema),
} }
} }
@ -106,7 +106,7 @@ pub trait CliReturnValue: sealed::SealedCliReturnValue {
} }
macro_rules! impl_cli_return { macro_rules! impl_cli_return {
($(for $ty:ty => $type:ident);+) => {$( ($(for $ty:ty => $type:ident);+;) => {$(
impl CliReturnValue for $ty { impl CliReturnValue for $ty {
fn kind() -> SchemaKind { fn kind() -> SchemaKind {
SchemaKind::$type SchemaKind::$type
@ -118,8 +118,9 @@ macro_rules! impl_cli_return {
impl_cli_return!( impl_cli_return!(
for String => String; for String => String;
for bool => Bool; for bool => Bool;
for isize => INum; for isize => IInt;
for usize => UNum for usize => UInt;
for f64 => Num;
); );
mod sealed { mod sealed {
@ -127,7 +128,7 @@ mod sealed {
macro_rules! impl_ { macro_rules! impl_ {
($($name:ty),+) => {$(impl SealedCliReturnValue for $name{})+}; ($($name:ty),+) => {$(impl SealedCliReturnValue for $name{})+};
} }
impl_!(String, bool, usize, isize); impl_!(String, bool, usize, isize, f64);
} }
mod error { mod error {
@ -149,7 +150,9 @@ mod error {
ExpectedValue(String, SchemaKind), ExpectedValue(String, SchemaKind),
INan(String), INan(String),
UNan(String), UNan(String),
NNan(String),
CombinedShortWithValue(String), CombinedShortWithValue(String),
InvalidUtf8(OsString), InvalidUtf8(OsString),
HelpPage,
} }
} }

View file

@ -23,7 +23,6 @@
/// ``` /// ```
#[macro_export] #[macro_export]
macro_rules! arg { macro_rules! arg {
// implicit optional
($name:ident: $long:literal, $short:literal -> $result:ty) => { ($name:ident: $long:literal, $short:literal -> $result:ty) => {
arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result); arg!(@$name: ($long, ::std::option::Option::Some($short)) -> $result);
}; };

View file

@ -93,6 +93,9 @@ fn parse_long(
long: &str, long: &str,
args: &mut impl Iterator<Item = OsString>, args: &mut impl Iterator<Item = OsString>,
) -> Result<()> { ) -> Result<()> {
if long == "help" {
return Err(CallError::HelpPage);
}
let command = schema let command = schema
.long(long) .long(long)
.ok_or_else(|| CallError::LongFlagNotFound(long.to_string()))?; .ok_or_else(|| CallError::LongFlagNotFound(long.to_string()))?;
@ -115,7 +118,7 @@ fn parse_value(
.map_err(CallError::InvalidUtf8)?; .map_err(CallError::InvalidUtf8)?;
results.insert(long, Box::new(string)); results.insert(long, Box::new(string));
} }
SchemaKind::INum => { SchemaKind::IInt => {
let integer = args let integer = args
.next() .next()
.ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))? .ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))?
@ -125,7 +128,7 @@ fn parse_value(
.map_err(|_| CallError::INan(long.to_string()))?; .map_err(|_| CallError::INan(long.to_string()))?;
results.insert(long, Box::new(integer)) results.insert(long, Box::new(integer))
} }
SchemaKind::UNum => { SchemaKind::UInt => {
let integer = args let integer = args
.next() .next()
.ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))? .ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))?
@ -135,6 +138,16 @@ fn parse_value(
.map_err(|_| CallError::UNan(long.to_string()))?; .map_err(|_| CallError::UNan(long.to_string()))?;
results.insert(long, Box::new(integer)) results.insert(long, Box::new(integer))
} }
SchemaKind::Num => {
let float = args
.next()
.ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))?
.into_string()
.map_err(CallError::InvalidUtf8)?
.parse::<f64>()
.map_err(|_| CallError::NNan(long.to_string()))?;
results.insert(long, Box::new(float))
}
SchemaKind::Bool => { SchemaKind::Bool => {
results.insert(long, Box::new(true)); results.insert(long, Box::new(true));
} }

View file

@ -1,7 +1,7 @@
use crate::error::CallError; use crate::error::CallError;
use crate::schema::SchemaKind; use crate::schema::{Schema, SchemaKind};
pub fn report(err: CallError) -> ! { pub fn report(err: CallError, schema: &Schema) -> ! {
match err { match err {
CallError::ShortFlagNotFound(arg) => println!("error: argument '{}' does not exist.", arg), CallError::ShortFlagNotFound(arg) => println!("error: argument '{}' does not exist.", arg),
CallError::LongFlagNotFound(arg) => println!("error: argument '{}' does not exist.", arg), CallError::LongFlagNotFound(arg) => println!("error: argument '{}' does not exist.", arg),
@ -12,15 +12,35 @@ pub fn report(err: CallError) -> ! {
match kind { match kind {
SchemaKind::String => "string", SchemaKind::String => "string",
SchemaKind::Bool => unreachable!(), SchemaKind::Bool => unreachable!(),
SchemaKind::INum => "integer", SchemaKind::IInt => "integer",
SchemaKind::UNum => "positive integer", SchemaKind::UInt => "positive integer",
SchemaKind::Num => "number"
} }
) )
} }
CallError::INan(arg) => println!("error: argument '{}' expected a positive integer value, but got an invalid positive integer.", arg), CallError::INan(arg) => println!("error: argument '{}' expected a positive integer value, but got an invalid positive integer.", arg),
CallError::UNan(arg) => println!("error: argument '{}' expected an integer value, but got an invalid integer.", arg), CallError::UNan(arg) => println!("error: argument '{}' expected an integer value, but got an invalid integer.", arg),
CallError::NNan(arg) => println!("error: argument '{}' expected a number value, but got an invalid number.", arg),
CallError::CombinedShortWithValue(arg) => println!("error: using argument expecting value '{}' in position where only flags are allowed", arg), CallError::CombinedShortWithValue(arg) => println!("error: using argument expecting value '{}' in position where only flags are allowed", arg),
CallError::InvalidUtf8(os_str) => println!("error: invalid utf8: '{}'", os_str.to_string_lossy()), CallError::InvalidUtf8(os_str) => println!("error: invalid utf8: '{}'", os_str.to_string_lossy()),
CallError::HelpPage => {
println!("Options:");
for option in schema.arguments() {
print!("--{} ", option.long);
if let Some(short) = option.short {
print!("(-{}) ", short);
}
match option.kind {
SchemaKind::String => print!("[Takes a value]"),
SchemaKind::Bool => {}
SchemaKind::IInt => print!("[Takes an integer]"),
SchemaKind::UInt => print!("[Takes a positive integer]"),
SchemaKind::Num => print!("[Takes a number]"),
}
println!();
}
std::process::exit(0);
}
} }
std::process::exit(1) std::process::exit(1)

View file

@ -15,8 +15,9 @@ use std::collections::HashMap;
pub enum SchemaKind { pub enum SchemaKind {
String, String,
Bool, Bool,
INum, IInt,
UNum, UInt,
Num,
} }
/// ///
@ -63,6 +64,10 @@ impl Schema {
self.longs.get(name) self.longs.get(name)
} }
pub fn arguments(&self) -> impl Iterator<Item = &SchemaCommand> {
self.longs.values()
}
fn add_short_command(&mut self, short_name: char, command: SchemaCommand) -> Result<()> { fn add_short_command(&mut self, short_name: char, command: SchemaCommand) -> Result<()> {
if self.shorts.insert(short_name, command).is_some() { if self.shorts.insert(short_name, command).is_some() {
Err(SchemaError::NameAlreadyExists(short_name.to_string())) Err(SchemaError::NameAlreadyExists(short_name.to_string()))