#![deny(missing_docs)]
use std::fmt;
use std::str::FromStr;
use std::ffi::{OsStr, OsString};
pub trait ParseArg: Sized {
type Error: fmt::Display;
fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error>;
fn describe_type<W: fmt::Write>(writer: W) -> fmt::Result;
fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> {
Self::parse_arg(&arg)
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum ParseArgError<E> {
FromStr(E),
InvalidUtf8,
}
impl<E: fmt::Display> fmt::Display for ParseArgError<E> {
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"),
}
}
}
impl<E: fmt::Display> fmt::Debug for ParseArgError<E> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(self, f)
}
}
impl<E: fmt::Display> From<E> for ParseArgError<E> {
fn from(error: E) -> Self {
ParseArgError::FromStr(error)
}
}
pub trait ParseArgFromStr: FromStr where <Self as FromStr>::Err: fmt::Display {
fn describe_type<W: fmt::Write>(writer: W) -> fmt::Result;
}
impl<T> ParseArg for T where T: ParseArgFromStr, <T as FromStr>::Err: fmt::Display {
type Error = ParseArgError<<T as FromStr>::Err>;
fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> {
arg.to_str().ok_or(ParseArgError::InvalidUtf8)?.parse().map_err(Into::into)
}
fn describe_type<W: fmt::Write>(writer: W) -> fmt::Result {
<Self as ParseArgFromStr>::describe_type(writer)
}
}
impl ParseArg for String {
type Error = ParseArgError<std::string::ParseError>;
fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> {
arg.to_str().ok_or(ParseArgError::InvalidUtf8).map(Into::into).map_err(Into::into)
}
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a UTF-8 encoded string")
}
fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> {
arg.into_string().map_err(|_| ParseArgError::InvalidUtf8)
}
}
impl ParseArg for OsString {
type Error = std::string::ParseError;
fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> {
Ok(arg.into())
}
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "any string")
}
fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> {
Ok(arg)
}
}
impl ParseArg for std::path::PathBuf {
type Error = std::string::ParseError;
fn parse_arg(arg: &OsStr) -> Result<Self, Self::Error> {
Ok(arg.into())
}
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a path")
}
fn parse_owned_arg(arg: OsString) -> Result<Self, Self::Error> {
Ok(arg.into())
}
}
macro_rules! impl_unsigned {
($($type:ty),*) => {
$(
impl ParseArgFromStr for $type {
fn describe_type<W: fmt::Write>(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<W: fmt::Write>(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<W: fmt::Write>(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<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "an IP address (either version 4 or 6)")
}
}
impl ParseArgFromStr for std::net::Ipv4Addr {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a version 4 IP address")
}
}
impl ParseArgFromStr for std::net::Ipv6Addr {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a version 6 IP address")
}
}
impl ParseArgFromStr for std::net::SocketAddr {
fn describe_type<W: fmt::Write>(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<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a version 4 socket address (IP:port)")
}
}
impl ParseArgFromStr for std::net::SocketAddrV6 {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a version 6 socket address (IP:port)")
}
}
impl ParseArgFromStr for bool {
fn describe_type<W: fmt::Write>(mut writer: W) -> fmt::Result {
write!(writer, "a boolean (true or false)")
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum ValueError<E> {
InvalidValue(E),
MissingValue,
}
impl<E> ValueError<E> {
pub fn map_or<R, F: FnOnce(E) -> R>(self, default: R, f: F) -> R {
match self {
ValueError::InvalidValue(error) => f(error),
ValueError::MissingValue => default,
}
}
}
pub trait Arg: Sized + AsRef<OsStr> {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error>;
}
impl Arg for OsString {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error> {
T::parse_owned_arg(self)
}
}
impl<'a> Arg for &'a OsStr {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error> {
T::parse_arg(self)
}
}
impl<'a, U: 'a + Arg + Copy> Arg for &'a U {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error> {
U::parse(*self)
}
}
impl Arg for String {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error> {
T::parse_owned_arg(self.into())
}
}
impl<'a> Arg for &'a str {
fn parse<T: ParseArg>(self) -> Result<T, <T as ParseArg>::Error> {
T::parse_arg(self.as_ref())
}
}
pub fn match_arg<T: ParseArg, S: AsRef<OsStr>, I>(name: &str, arg: S, next: I) -> Option<Result<T, ValueError<<T as ParseArg>::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::<T>(name.as_ref(), arg.as_ref()).map(|result| result.map_err(ValueError::InvalidValue))
}
}
#[inline]
pub fn iter_short<'a, T>(arg: &'a T) -> Option<ShortIter<'a>> where T: AsRef<OsStr> + ?Sized {
iter_short_internal(arg.as_ref())
}
#[cfg(unix)]
fn iter_short_internal<'a>(arg: &'a OsStr) -> Option<ShortIter<'a>> {
use ::std::os::unix::ffi::OsStrExt;
let slice = arg.as_bytes();
if slice.len() < 2 {
return None;
}
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<ShortIter<'a>> {
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
}
}
pub struct ShortIter<'a> {
#[cfg(unix)]
iter: std::slice::Iter<'a, u8>,
#[cfg(windows)]
iter: std::iter::Peekable<std::os::windows::ffi::EncodeWide<'a>>,
_force_private: (),
}
impl<'a> Iterator for ShortIter<'a> {
type Item = char;
fn next(&mut self) -> Option<Self::Item> {
self.next_impl()
}
}
impl<'a> ShortIter<'a> {
pub fn parse_remaining<T: ParseArg, I>(self, iter: I) -> Result<T, ValueError<T::Error>> where I: IntoIterator, I::Item: Arg {
self.parse_remaining_internal(iter)
}
#[cfg(unix)]
fn parse_remaining_internal<T: ParseArg, I>(self, iter: I) -> Result<T, ValueError<T::Error>> 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<T: ParseArg, I>(mut self, iter: I) -> Result<T, ValueError<T::Error>> 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::<Vec<_>>();
let arg = OsString::from_wide(&arg);
arg
.parse()
.map_err(ValueError::InvalidValue)
}
#[cfg(unix)]
fn next_impl(&mut self) -> Option<<Self as Iterator>::Item> {
self.iter.next().map(|&c| c.into())
}
#[cfg(windows)]
fn next_impl(&mut self) -> Option<<Self as Iterator>::Item> {
self.iter.next().and_then(|c| {
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<T: ParseArg>(name: &OsStr, arg: &OsStr) -> Option<Result<T, <T as ParseArg>::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<T: ParseArg>(name: &OsStr, arg: &OsStr) -> Option<Result<T, <T as ParseArg>::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'=')) {
let arg = arg_iter.collect::<Vec<_>>();
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::<u32, _, _>("--foo", "--bar", std::iter::empty::<&str>()), None);
assert_eq!(::match_arg::<u32, _, _>("--foo", "--bar", &["--foo"]), None);
assert_eq!(::match_arg::<u32, _, _>("--foo", "--foo", std::iter::empty::<&str>()), Some(Err(::ValueError::MissingValue)));
assert_eq!(::match_arg::<u32, _, _>("--foo", "--foo", &["--foo"]), Some("--foo".parse::<u32>().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue)));
assert_eq!(::match_arg::<u32, _, _>("--foo", "--foo", &["42"]), Some(Ok(42)));
assert_eq!(::match_arg::<u32, _, _>("--foo", "--bar=", std::iter::empty::<&str>()), None);
assert_eq!(::match_arg::<u32, _, _>("--foo", "--bar=", &["--foo"]), None);
assert_eq!(::match_arg::<u32, _, _>("--foo", "--foo=", std::iter::empty::<&str>()), Some("".parse::<u32>().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue)));
let mut iter = ["--foo"].iter();
assert_eq!(::match_arg::<u32, _, _>("--foo", "--foo=--foo", &mut iter), Some("--foo".parse::<u32>().map_err(::ParseArgError::FromStr).map_err(::ValueError::InvalidValue)));
let mut iter = ["47"].iter();
assert_eq!(::match_arg::<u32, _, _>("--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::<u32, _>(::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::<u32, _>(&["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::<u32, _>(::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::<u32, _>(&["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::<u32, _>(::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::<u32, _>(&["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::<u32, _>(&["24"]) {
Ok(val) => panic!("Parsed unexpected vlaue: {}, parsing should've failed", val),
Err(ValueError::MissingValue) => panic!("Value shouldn't be missing"),
Err(ValueError::InvalidValue(_)) => (),
}
}
}