parse_arg-0.1.4/.gitignore010064400017500001750000000000361337006011300136610ustar0000000000000000/target **/*.rs.bk Cargo.lock parse_arg-0.1.4/Cargo.toml.orig010064400017500001750000000010031353521256100145640ustar0000000000000000[package] name = "parse_arg" version = "0.1.4" authors = ["Martin Habovstiak "] description = "Traits and implementations for parsing command-line arguments." homepage = "https://github.com/Kixunil/parse_arg" repository = "https://github.com/Kixunil/parse_arg" readme = "README.md" keywords = ["arguments", "parsing", "string", "osstring", "osstr"] categories = ["command-line-interface", "parsing", "rust-patterns"] license = "MITNFA" maintenance = { status = "passively-maintained" } parse_arg-0.1.4/Cargo.toml0000644000000017360000000000000110450ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] name = "parse_arg" version = "0.1.4" authors = ["Martin Habovstiak "] description = "Traits and implementations for parsing command-line arguments." homepage = "https://github.com/Kixunil/parse_arg" readme = "README.md" keywords = ["arguments", "parsing", "string", "osstring", "osstr"] categories = ["command-line-interface", "parsing", "rust-patterns"] license = "MITNFA" repository = "https://github.com/Kixunil/parse_arg" parse_arg-0.1.4/README.md010064400017500001750000000037211353417377100131750ustar0000000000000000Parse arg ========= Traits and implementations for parsing command-line arguments. About ----- This crate provides traits and implementations for parsing command-line arguments. The core of the crate is `ParseArg` trait. It works much like `FromStr` trait, but with these differences: * It operates on `&OsStr` instead of `&str`, thus allowing wider range of possible inputs. * It provides `parse_owned_arg()` method which can be specialized to avoid allocations. * It requires the implementor to provide `describe_type()` to print human-readable description. of expected input. * It requires the error type to implement `Display` in order to enable user-friendly interface. Further, the crate provides `ParseArgFromStr` trait, which causes any type implementing it to auto-implement `ParseArg` trait. This is handy when implementing `ParseArg` for types that already have `FromStr` implemented, so that boilerplate is reduced. Since matching both `--foo VAL` and `--foo=VAL` arguments is common and it's not an easy task to implement on top of `OsStr`, due to limitation of `std`, this crate also provides a function to match and parse such arguments. Further, since many programs support `-xVAL` style parameters and short switches grouped into a single argument, which isn't easy to achieve using `OsStr` either, this crate provides a helper for this too. Contibutions ------------ I will merge new implementations liberally, provided these conditions are met: * The type is either in `std` or in another crate which is added as an *optional* dependency. * The description of the type is gramatically correct, readable explanation of the type, that can be appended to the string "The input must be " without sounding unnatural. * The user must be able to know how to formt the input based on the description without any googling. In case of doubt provide more precise description in parentheses. * Your contribution is licensed under MIT or MITNFA license. License ------- MITNFA parse_arg-0.1.4/src/lib.rs010064400017500001750000000602231353470511200136070ustar0000000000000000//! This crate provides traits and implementations for parsing command-line arguments. //! The core of the crate is `ParseArg` trait. It works much like `FromStr` trait, but with //! these differences: //! //! * It operates on `&OsStr` instead of `&str`, thus allowing wider range of possible inputs. //! * It provides `parse_owned_arg()` method which can be specialized to avoid allocations. //! * It requires the implementor to provide `describe_type()` to print human-readable description. //! of expected input. //! * It requires the error type to implement `Display` in order to enable user-friendly interface. //! //! Further, the crate provides `ParseArgFromStr` trait, which causes any type implementing it to //! auto-implement `ParseArg` trait. This is handy when implementing `ParseArg` for types that //! already have `FromStr` implemented, so that boilerplate is reduced. #![deny(missing_docs)] use std::fmt; use std::str::FromStr; use std::ffi::{OsStr, OsString}; /// Defines an interface for types that can be created by parsing command-line argument. /// /// This trait is similar to `FromStr`. See the crate documentation for list of importatn /// differences. pub trait ParseArg: Sized { /// Type returned in `Err` variant of `Result` when parsing fails. type Error: fmt::Display; /// Parses the argument. fn parse_arg(arg: &OsStr) -> Result; /// Writes human-readable description of the type to the writer. /// /// The description should be in English composed in such way that appending it to string "The /// input must be " sounds natural. E.g. if the description is "a number", the resulting phrase /// will be "The input must be a number". /// /// This way, it can be used as a documentation/hint for the user. fn describe_type(writer: W) -> fmt::Result; /// Parses the argument consuming it. /// /// Implementors are encouraged to specialize this method if the resulting implementation is /// more performant - e.g. if it avoids allocation. /// /// The users are encouraged to use this method instead of `parse_arg` if they own the string /// and will not need it after call to this function. (Typical when working with /// `std::env::args_os()`.) fn parse_owned_arg(arg: OsString) -> Result { Self::parse_arg(&arg) } } /// Possible error when parsing certain arguments. /// /// This is used for bridging implementations of `FromStr`, because they require UTF-8 encoded inputs. #[derive(Copy, Clone, Eq, PartialEq, Hash)] pub enum ParseArgError { /// Parsing as implemented by `FromStr` failed. FromStr(E), /// The input isn't UTF-8 encoded. InvalidUtf8, } impl fmt::Display for ParseArgError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match self { ParseArgError::FromStr(err) => fmt::Display::fmt(err, f), ParseArgError::InvalidUtf8 => write!(f, "invalid UTF-8 encoding"), } } } /// `Debug` is implemented via `Display` in order to make using `?` operator in main nice. impl fmt::Debug for ParseArgError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(self, f) } } impl From for ParseArgError { fn from(error: E) -> Self { ParseArgError::FromStr(error) } } /// Shorthand for implementing `ParseArg` for types that already implement `FromStr`. /// /// This trait allows to implement `ParseArg` cheaply, just by providing the description. Prefer /// this approach if your type already impls `FromStr` without copying the string. In case the /// implementation can be made more performant by directly implementing `ParseArg`, prefer direct /// implementation. (This is what `String` does for example.) /// /// **This trait should only be implemented - do not use it as a bound! Use `ParseArg` as a bound /// because it is more general and provides same features.** pub trait ParseArgFromStr: FromStr where ::Err: fmt::Display { /// Writes human-readable description of the type to the writer. /// /// For full information, read the documentation for `ParseArgs::describe_type`. /// /// `ParseArgs::describe_type` is delegated to this method when `ParseArgFromStr` is /// implmented. fn describe_type(writer: W) -> fmt::Result; } impl ParseArg for T where T: ParseArgFromStr, ::Err: fmt::Display { type Error = ParseArgError<::Err>; fn parse_arg(arg: &OsStr) -> Result { arg.to_str().ok_or(ParseArgError::InvalidUtf8)?.parse().map_err(Into::into) } fn describe_type(writer: W) -> fmt::Result { ::describe_type(writer) } } /// Optimized implementation - doesn't allocate in `parse_owned_arg`. impl ParseArg for String { type Error = ParseArgError; fn parse_arg(arg: &OsStr) -> Result { arg.to_str().ok_or(ParseArgError::InvalidUtf8).map(Into::into).map_err(Into::into) } fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a UTF-8 encoded string") } fn parse_owned_arg(arg: OsString) -> Result { arg.into_string().map_err(|_| ParseArgError::InvalidUtf8) } } /// This implementation is a no-op or clone, since `OsString` is already `OsString`. impl ParseArg for OsString { type Error = std::string::ParseError; fn parse_arg(arg: &OsStr) -> Result { Ok(arg.into()) } fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "any string") } fn parse_owned_arg(arg: OsString) -> Result { Ok(arg) } } /// To my knowledge this is a no-op or clone. impl ParseArg for std::path::PathBuf { type Error = std::string::ParseError; fn parse_arg(arg: &OsStr) -> Result { Ok(arg.into()) } fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a path") } fn parse_owned_arg(arg: OsString) -> Result { Ok(arg.into()) } } macro_rules! impl_unsigned { ($($type:ty),*) => { $( impl ParseArgFromStr for $type { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a non-negative integer up to {}", <$type>::max_value()) } } )* } } macro_rules! impl_signed { ($($type:ty),*) => { $( impl ParseArgFromStr for $type { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "an integer at least {} and up to {}", <$type>::min_value(), <$type>::max_value()) } } )* } } macro_rules! impl_float { ($($type:ident),*) => { $( impl ParseArgFromStr for $type { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a real number at least {} and up to {}", std::$type::MIN, std::$type::MAX) } } )* } } impl_unsigned! { u8, u16, u32, u64, u128, usize } impl_signed! { i8, i16, i32, i64, i128, isize } impl_float! { f32, f64 } impl ParseArgFromStr for std::net::IpAddr { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "an IP address (either version 4 or 6)") } } impl ParseArgFromStr for std::net::Ipv4Addr { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a version 4 IP address") } } impl ParseArgFromStr for std::net::Ipv6Addr { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a version 6 IP address") } } impl ParseArgFromStr for std::net::SocketAddr { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a version 4 or 6 network socket address (IP:port)") } } impl ParseArgFromStr for std::net::SocketAddrV4 { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a version 4 socket address (IP:port)") } } impl ParseArgFromStr for std::net::SocketAddrV6 { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a version 6 socket address (IP:port)") } } impl ParseArgFromStr for bool { fn describe_type(mut writer: W) -> fmt::Result { write!(writer, "a boolean (true or false)") } } /// Error that can occur durin parsing command-line argument. #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub enum ValueError { /// The value couldn't be parsed because of the invalid format. InvalidValue(E), /// The value to the argument is missing - too few arguments. MissingValue, } impl ValueError { /// Applies function to the contained error (if any), or returns the provided default (if not). /// /// This is practically the same thing what `Option` would do. pub fn map_or R>(self, default: R, f: F) -> R { match self { ValueError::InvalidValue(error) => f(error), ValueError::MissingValue => default, } } } /// A type that can be parsed. /// /// Usually `&OsStr` or `OsString`. It's used to automatically pick the right /// method if `ParseArg` trait. pub trait Arg: Sized + AsRef { /// Parses the argument from `self` using appropriate method. /// /// In case `self` is `OsString` or can be converted to it at no cost /// `ParseArg::parse_owned_arg()` is used. Otherwise `ParseArg::parse_arg()` is used. fn parse(self) -> Result::Error>; } impl Arg for OsString { fn parse(self) -> Result::Error> { T::parse_owned_arg(self) } } impl<'a> Arg for &'a OsStr { fn parse(self) -> Result::Error> { T::parse_arg(self) } } impl<'a, U: 'a + Arg + Copy> Arg for &'a U { fn parse(self) -> Result::Error> { U::parse(*self) } } /// Using this with `std::env::args()` is not a good practice, but it may be /// useful for testing. impl Arg for String { fn parse(self) -> Result::Error> { T::parse_owned_arg(self.into()) } } /// Using this with `std::env::args()` is not a good practice, but it may be /// useful for testing. impl<'a> Arg for &'a str { fn parse(self) -> Result::Error> { T::parse_arg(self.as_ref()) } } /// Checks whether given arg matches the `name` or begins with `name=` and parses it /// appropriately. /// /// In case `arg` is equal to name, the next argument is pulled from `next` iterator and parsed using /// the most efficient method. /// In case `arg` begins with `name=`, it strips the `name=` prefix and parses the rest. /// /// Note: due to a limitation of `std`, Windows implementation is slower than Unix one in `name=` /// case. The difference is one-two allocations (depending on parsed type). This probably won't be /// noticable for the user. pub fn match_arg, I>(name: &str, arg: S, next: I) -> Option::Error>>> where I: IntoIterator, I::Item: Arg { if *(arg.as_ref()) == *name { if let Some(arg) = next.into_iter().next() { Some(arg.parse().map_err(ValueError::InvalidValue)) } else { Some(Err(ValueError::MissingValue)) } } else { check_prefix::(name.as_ref(), arg.as_ref()).map(|result| result.map_err(ValueError::InvalidValue)) } } /// Creates an iterator of short arguments if the input is in the form `-abc`. /// /// This is a helper for parsing short arguments in the same way mny GNU commands support. /// It enables putting several flags into a single argument and enables the last one to be /// a short parameter with a walue and the value might be either concatenated or put as the /// next argument. /// /// In order to use this, one would call this function and if it returned `Some(iter)`, then /// iterate `iter` and match returned chars. If a char representing a parameter with value /// is returned from `next`, `.parse_remaining()` method should be called on the iterator. /// It will attempt to parse the value from remaining part, if present. If there's none, it'll /// attempt to parse it from the following argument pulled from the parameter. /// /// This is how `configure_me` uses this feature, but you can use it without `configure_me`. #[inline] pub fn iter_short<'a, T>(arg: &'a T) -> Option> where T: AsRef + ?Sized { iter_short_internal(arg.as_ref()) } #[cfg(unix)] fn iter_short_internal<'a>(arg: &'a OsStr) -> Option> { use ::std::os::unix::ffi::OsStrExt; let slice = arg.as_bytes(); // Needed to avoid panic and this can't be an option anyway if slice.len() < 2 { return None; } // Check for things beginning with -- if slice[1] == b'-' { return None; } let mut arg_iter = slice.iter(); if *arg_iter.next()? == b'-' { Some(ShortIter { iter: arg_iter, _force_private: (), }) } else { None } } #[cfg(windows)] fn iter_short_internal<'a>(arg: &'a OsStr) -> Option> { use ::std::os::windows::ffi::OsStrExt; let mut iter = arg.encode_wide(); if iter.next()? == u16::from(b'-') { let mut iter = iter.peekable(); if *iter.peek()? != u16::from(b'-') { Some(ShortIter { iter, _force_private: (), }) } else { None } } else { None } } /// An iterator of short options. /// /// See the documentation for `iter_short` for more details. pub struct ShortIter<'a> { #[cfg(unix)] iter: std::slice::Iter<'a, u8>, #[cfg(windows)] iter: std::iter::Peekable>, // In case the platform is neither unix nor windows, we want to keep this private _force_private: (), } impl<'a> Iterator for ShortIter<'a> { type Item = char; fn next(&mut self) -> Option { self.next_impl() } } impl<'a> ShortIter<'a> { /// Parses the remaining string of the argument. /// /// This is used in case of `-xVAL` style arguments. If `VAL` is missing, it is pulled from /// `iter`. If that's missing too, Err(ValueError::MissingValue) is returned. /// /// See the doc of `iter_short()` for more context. /// /// While this method could take `&self`, consuming expresses that the iteration should end. pub fn parse_remaining(self, iter: I) -> Result> where I: IntoIterator, I::Item: Arg { self.parse_remaining_internal(iter) } #[cfg(unix)] fn parse_remaining_internal(self, iter: I) -> Result> where I: IntoIterator, I::Item: Arg { use ::std::os::unix::ffi::OsStrExt; let slice = self.iter.as_slice(); if slice.len() == 0 { return iter .into_iter() .next() .map_or(Err(ValueError::MissingValue), |val| val.parse().map_err(ValueError::InvalidValue)); } OsStr::from_bytes(slice) .parse() .map_err(ValueError::InvalidValue) } #[cfg(windows)] fn parse_remaining_internal(mut self, iter: I) -> Result> where I: IntoIterator, I::Item: Arg { use ::std::os::windows::ffi::OsStringExt; if self.iter.peek().is_none() { return iter .into_iter() .next() .map_or(Err(ValueError::MissingValue), |val| val.parse().map_err(ValueError::InvalidValue)); } let arg = self.iter.collect::>(); let arg = OsString::from_wide(&arg); arg .parse() .map_err(ValueError::InvalidValue) } #[cfg(unix)] fn next_impl(&mut self) -> Option<::Item> { self.iter.next().map(|&c| c.into()) } #[cfg(windows)] fn next_impl(&mut self) -> Option<::Item> { self.iter.next().and_then(|c| { // We assume all options are ASCII chars, which should be decoded as exactly one char. // If they're not, we stop iteration. // // TODO: might be better to return error instead. let mut decoder = std::char::decode_utf16(std::iter::once(c)); let result = decoder.next()?.ok()?; if decoder.next().is_none() { Some(result) } else { None } }) } } #[cfg(unix)] fn check_prefix(name: &OsStr, arg: &OsStr) -> Option::Error>> { use ::std::os::unix::ffi::OsStrExt; let mut arg_iter = arg.as_bytes().iter(); if name.as_bytes().iter().zip(&mut arg_iter).all(|(a, b)| *a == *b) { if arg_iter.next() == Some(&b'=') { let arg = OsStr::from_bytes(arg_iter.as_slice()); Some(T::parse_arg(arg)) } else { None } } else { None } } #[cfg(windows)] fn check_prefix(name: &OsStr, arg: &OsStr) -> Option::Error>> { use ::std::os::windows::ffi::{OsStrExt, OsStringExt}; let mut arg_iter = arg.encode_wide(); if name.encode_wide().zip(&mut arg_iter).all(|(a, b)| a == b) { if arg_iter.next() == Some(u16::from(b'=')) { // If you don't like these two allocations, go annoy Rust libs team // with RFC to implement matching and slicing on `OsStr`... let arg = arg_iter.collect::>(); let arg = OsString::from_wide(&arg); Some(T::parse_owned_arg(arg)) } else { None } } else { None } } #[cfg(test)] mod tests { #[test] fn numbers() { use ::ParseArg; let val: u8 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: u16 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: u32 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: u64 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: u128 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: usize = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: i8 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: i16 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: i32 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: i64 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: i128 = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: isize = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: i8 = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: i16 = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: i32 = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: i64 = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: i128 = ParseArg::parse_arg("-42".as_ref()).unwrap(); assert_eq!(val, -42); let val: isize = ParseArg::parse_arg("42".as_ref()).unwrap(); assert_eq!(val, 42); let val: f32 = ParseArg::parse_arg("42.42".as_ref()).unwrap(); assert_eq!(val, 42.42); let val: f64 = ParseArg::parse_arg("42.42".as_ref()).unwrap(); assert_eq!(val, 42.42); } #[test] fn match_args() { assert_eq!(::match_arg::("--foo", "--bar", std::iter::empty::<&str>()), None); assert_eq!(::match_arg::("--foo", "--bar", &["--foo"]), None); assert_eq!(::match_arg::("--foo", "--foo", std::iter::empty::<&str>()), Some(Err(::ValueError::MissingValue))); assert_eq!(::match_arg::("--foo", "--foo", &["--foo"]), Some("--foo".parse::().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue))); assert_eq!(::match_arg::("--foo", "--foo", &["42"]), Some(Ok(42))); assert_eq!(::match_arg::("--foo", "--bar=", std::iter::empty::<&str>()), None); assert_eq!(::match_arg::("--foo", "--bar=", &["--foo"]), None); assert_eq!(::match_arg::("--foo", "--foo=", std::iter::empty::<&str>()), Some("".parse::().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue))); let mut iter = ["--foo"].iter(); assert_eq!(::match_arg::("--foo", "--foo=--foo", &mut iter), Some("--foo".parse::().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue))); let mut iter = ["47"].iter(); assert_eq!(::match_arg::("--foo", "--foo=42", &mut iter), Some(Ok(42))); assert_eq!(iter.next(), Some(&"47")); } #[test] fn iter_short() { use ::ValueError; assert!(::iter_short("").is_none()); assert!(::iter_short("-").is_none()); assert!(::iter_short("--").is_none()); assert!(::iter_short("--a").is_none()); assert!(::iter_short("--ab").is_none()); assert_eq!(::iter_short("-a").expect("Iter").next().expect("next"), 'a'); let mut iter = ::iter_short("-ab").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.next().expect("next"), 'b'); assert!(iter.next().is_none()); let mut iter = ::iter_short("-abcde").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.next().expect("next"), 'b'); assert_eq!(iter.next().expect("next"), 'c'); assert_eq!(iter.next().expect("next"), 'd'); assert_eq!(iter.next().expect("next"), 'e'); assert!(iter.next().is_none()); let mut iter = ::iter_short("-a").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.parse_remaining::(::std::iter::empty::<&str>()), Err(ValueError::MissingValue)); let mut iter = ::iter_short("-a").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.parse_remaining::(&["42"]).expect("Failed to parse"), 42); let mut iter = ::iter_short("-a42").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.parse_remaining::(::std::iter::empty::<&str>()).expect("Failed to parse"), 42); let mut iter = ::iter_short("-a42").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.parse_remaining::(&["24"]).expect("Failed to parse"), 42); let mut iter = ::iter_short("-ab42").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.next().expect("next"), 'b'); assert_eq!(iter.parse_remaining::(::std::iter::empty::<&str>()).expect("Failed to parse"), 42); let mut iter = ::iter_short("-ab42").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.next().expect("next"), 'b'); assert_eq!(iter.parse_remaining::(&["24"]).expect("Failed to parse"), 42); let mut iter = ::iter_short("-abc").expect("Iter"); assert_eq!(iter.next().expect("next"), 'a'); assert_eq!(iter.next().expect("next"), 'b'); match iter.parse_remaining::(&["24"]) { Ok(val) => panic!("Parsed unexpected vlaue: {}, parsing should've failed", val), Err(ValueError::MissingValue) => panic!("Value shouldn't be missing"), Err(ValueError::InvalidValue(_)) => (), } } } parse_arg-0.1.4/.cargo_vcs_info.json0000644000000001120000000000000130320ustar00{ "git": { "sha1": "c60e44a0a3a8edd8059c51f0f25bdb51e47e9b5a" } }