os string handling

This commit is contained in:
nora 2021-10-01 23:56:41 +02:00
parent 34bddb59a6
commit a393fce416
4 changed files with 28 additions and 15 deletions

View file

@ -1,8 +1,3 @@
# badargs # badargs
A Rust fully type-safe argument parser without proc-macros
Usability comes after that A zero-dependency full type-safe argument parser.
oh and it's also not even close to being usable in any way at all so there's that
oh and also 0 dependencies btw

View file

@ -55,9 +55,9 @@ pub fn badargs<S>() -> BadArgs
where where
S: IntoSchema, S: IntoSchema,
{ {
let arg_schema = Schema::create::<S>().expect("Invalid schema!"); let arg_schema = Schema::create::<S>().expect("Invalid schema");
let args = CliArgs::from_args(&arg_schema, std::env::args()); 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),
@ -132,6 +132,7 @@ mod sealed {
mod error { mod error {
use crate::schema::SchemaKind; use crate::schema::SchemaKind;
use std::ffi::OsString;
/// Invalid schema /// Invalid schema
#[derive(Debug, Clone, Eq, PartialEq)] #[derive(Debug, Clone, Eq, PartialEq)]
@ -149,5 +150,6 @@ mod error {
INan(String), INan(String),
UNan(String), UNan(String),
CombinedShortWithValue(String), CombinedShortWithValue(String),
InvalidUtf8(OsString),
} }
} }

View file

@ -2,6 +2,7 @@ use crate::error::CallError;
use crate::schema::{Schema, SchemaKind}; use crate::schema::{Schema, SchemaKind};
use std::any::Any; use std::any::Any;
use std::collections::HashMap; use std::collections::HashMap;
use std::ffi::OsString;
type Result<T> = std::result::Result<T, CallError>; type Result<T> = std::result::Result<T, CallError>;
@ -12,10 +13,14 @@ pub(crate) struct CliArgs {
} }
impl CliArgs { impl CliArgs {
pub fn from_args(schema: &Schema, mut args: impl Iterator<Item = String>) -> Result<Self> { pub fn from_args(schema: &Schema, mut args: impl Iterator<Item = OsString>) -> Result<Self> {
let mut result = Self::default(); let mut result = Self::default();
while let Some(arg) = args.next() { while let Some(arg) = args.next() {
let arg = arg
.into_string()
.map_err(|os_str| CallError::InvalidUtf8(os_str))?;
if let Some(long) = arg.strip_prefix("--") { if let Some(long) = arg.strip_prefix("--") {
parse_long(schema, &mut result, long, &mut args)?; parse_long(schema, &mut result, long, &mut args)?;
} else if let Some(shorts) = arg.strip_prefix('-') { } else if let Some(shorts) = arg.strip_prefix('-') {
@ -47,7 +52,7 @@ fn parse_shorts(
schema: &Schema, schema: &Schema,
results: &mut CliArgs, results: &mut CliArgs,
shorts: &str, shorts: &str,
args: &mut impl Iterator<Item = String>, args: &mut impl Iterator<Item = OsString>,
) -> Result<()> { ) -> Result<()> {
// there are kinds of short arguments // there are kinds of short arguments
// single shorts that takes values: `-o main` // single shorts that takes values: `-o main`
@ -88,7 +93,7 @@ fn parse_long(
schema: &Schema, schema: &Schema,
results: &mut CliArgs, results: &mut CliArgs,
long: &str, long: &str,
args: &mut impl Iterator<Item = String>, args: &mut impl Iterator<Item = OsString>,
) -> Result<()> { ) -> Result<()> {
let command = schema let command = schema
.long(long) .long(long)
@ -101,19 +106,23 @@ fn parse_value(
kind: SchemaKind, kind: SchemaKind,
results: &mut CliArgs, results: &mut CliArgs,
long: &'static str, long: &'static str,
args: &mut impl Iterator<Item = String>, args: &mut impl Iterator<Item = OsString>,
) -> Result<()> { ) -> Result<()> {
match kind { match kind {
SchemaKind::String => { SchemaKind::String => {
let string = args let string = args
.next() .next()
.ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))?; .ok_or_else(|| CallError::ExpectedValue(long.to_string(), kind))?
.into_string()
.map_err(|os_str| CallError::InvalidUtf8(os_str))?;
results.insert(long, Box::new(string)); results.insert(long, Box::new(string));
} }
SchemaKind::INum => { SchemaKind::INum => {
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))?
.into_string()
.map_err(|os_str| CallError::InvalidUtf8(os_str))?
.parse::<isize>() .parse::<isize>()
.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))
@ -122,6 +131,8 @@ fn parse_value(
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))?
.into_string()
.map_err(|os_str| CallError::InvalidUtf8(os_str))?
.parse::<usize>() .parse::<usize>()
.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))
@ -158,7 +169,11 @@ mod test {
} }
fn parse_args(args: &str) -> Result<CliArgs> { fn parse_args(args: &str) -> Result<CliArgs> {
CliArgs::from_args(&schema(), args.split_whitespace().map(|s| s.to_owned())) CliArgs::from_args(
&schema(),
args.split_whitespace()
.map(|s| OsString::from(s.to_owned())),
)
} }
#[test] #[test]

View file

@ -19,7 +19,8 @@ pub fn report(err: CallError) -> ! {
} }
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::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()),
} }
std::process::exit(1) std::process::exit(1)