use crate::CliError;
use std::ffi::OsString;
use std::num::{ParseFloatError, ParseIntError};
use std::path::PathBuf;
use std::str::FromStr;
pub trait ArgExt {
fn parse_str<N>(self, name: N) -> Result<String, CliError>
where
N: Into<String>;
fn parse_path<N>(self, name: N) -> Result<PathBuf, CliError>
where
N: Into<String>;
fn parse_osstr<N>(self, name: N) -> Result<OsString, CliError>
where
N: Into<String>;
fn parse_int<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseIntError>;
fn parse_float<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseFloatError>;
}
pub trait RequiredArgExt {
type Inner;
fn required<N>(self, name: N) -> Result<Self::Inner, CliError>
where
N: Into<String>;
}
impl ArgExt for Option<OsString> {
fn parse_str<N>(self, name: N) -> Result<String, CliError>
where
N: Into<String>,
{
let name = name.into();
self.ok_or_else(|| CliError::MissingValue(name.clone()))?
.into_string()
.map_err(|err| CliError::ParseStrError(name, err))
}
fn parse_path<N>(self, name: N) -> Result<PathBuf, CliError>
where
N: Into<String>,
{
Ok(self
.ok_or_else(|| CliError::MissingValue(name.into()))?
.into())
}
fn parse_osstr<N>(self, name: N) -> Result<OsString, CliError>
where
N: Into<String>,
{
self.ok_or_else(|| CliError::MissingValue(name.into()))
}
fn parse_int<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseIntError>,
{
let name = name.into();
self.clone().parse_str(&name).and_then(|string| {
string
.parse::<T>()
.map_err(|err| CliError::ParseIntError(name, self.unwrap(), err))
})
}
fn parse_float<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseFloatError>,
{
let name = name.into();
self.clone().parse_str(&name).and_then(|string| {
string
.parse::<T>()
.map_err(|err| CliError::ParseFloatError(name, self.unwrap(), err))
})
}
}
impl ArgExt for OsString {
fn parse_str<N>(self, name: N) -> Result<String, CliError>
where
N: Into<String>,
{
let name = name.into();
self.into_string()
.map_err(|err| CliError::ParseStrError(name, err))
}
fn parse_path<N>(self, _name: N) -> Result<PathBuf, CliError>
where
N: Into<String>,
{
Ok(self.into())
}
fn parse_osstr<N>(self, _name: N) -> Result<OsString, CliError>
where
N: Into<String>,
{
Ok(self)
}
fn parse_int<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseIntError>,
{
let name = name.into();
self.clone().parse_str(&name).and_then(|string| {
string
.parse::<T>()
.map_err(|err| CliError::ParseIntError(name, self, err))
})
}
fn parse_float<T, N>(self, name: N) -> Result<T, CliError>
where
N: Into<String>,
T: FromStr<Err = ParseFloatError>,
{
let name = name.into();
self.clone().parse_str(&name).and_then(|string| {
string
.parse::<T>()
.map_err(|err| CliError::ParseFloatError(name, self, err))
})
}
}
impl<T> RequiredArgExt for Option<T> {
type Inner = T;
fn required<N>(self, name: N) -> Result<Self::Inner, CliError>
where
N: Into<String>,
{
self.ok_or_else(|| CliError::MissingRequired(name.into()))
}
}
impl<T> RequiredArgExt for Vec<T> {
type Inner = Vec<T>;
fn required<N>(self, name: N) -> Result<Self::Inner, CliError>
where
N: Into<String>,
{
if self.is_empty() {
Err(CliError::MissingRequired(name.into()))
} else {
Ok(self)
}
}
}