#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
extern crate alloc;
use alloc::boxed::Box;
use alloc::string::String;
use alloc::vec::Vec;
mod error;
mod macros;
#[doc(hidden)]
pub use const_format::{map_ascii_case as __map_ascii_case, Case as __Case};
pub use core::iter::Peekable;
pub use error::Error;
pub type Result<T> = core::result::Result<T, Error>;
#[cfg(feature = "std")]
pub fn executable() -> String {
std::env::args()
.next()
.expect("first argument should always be the executable path")
}
pub trait Parsable: Sized {
type Output;
type FullOutput<T>;
fn make_full_output<T>(output: Self::Output, rest: T) -> Self::FullOutput<T>;
fn default() -> Option<Self> {
None
}
fn parse_from<S>(stream: &mut Peekable<S>) -> Result<Self::Output>
where
S: Iterator,
S::Item: AsRef<str> + Into<String>;
fn parse_all<A>(args: A) -> Result<Self::FullOutput<Vec<String>>>
where
A: IntoIterator,
A::Item: AsRef<str> + Into<String>,
{
let mut args = args.into_iter().skip(1).peekable();
let out = Self::parse_from(&mut args)?;
Ok(Self::make_full_output(
out,
args.map(|s| s.into()).collect(),
))
}
#[cfg(feature = "std")]
fn parse() -> Result<Self::FullOutput<Vec<String>>> {
Self::parse_all(std::env::args())
}
}
impl<T> Parsable for Box<T>
where
T: Parsable,
{
type Output = Box<T::Output>;
type FullOutput<Q> = (Self::Output, Q);
fn make_full_output<Q>(output: Self::Output, rest: Q) -> Self::FullOutput<Q> {
(output, rest)
}
#[cfg(feature = "std")]
fn parse() -> Result<Self::FullOutput<Vec<String>>> {
let mut args = std::env::args().skip(1).peekable();
let out = Self::parse_from(&mut args)?;
Ok(Self::make_full_output(out, args.collect()))
}
fn parse_from<S>(stream: &mut Peekable<S>) -> Result<Self::Output>
where
S: Iterator,
S::Item: AsRef<str> + Into<String>,
{
T::parse_from(stream).map(Box::new)
}
}
mod value {
extern crate alloc;
use alloc::ffi::CString;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
#[cfg(feature = "std")]
use std::{ffi::OsString, path::PathBuf};
use crate::{Error, Parsable, Peekable, Result};
macro_rules! impl_for {
($($t:ident $(<$gen:ident $(: $bound:ident $([$($gen_bound:tt)*])?)?>)?),*) => {$(
impl $(<$gen $(: $bound $(<$($gen_bound)*>)?)?>)? Parsable for $t $(<$gen>)? {
type Output = Self;
type FullOutput<T> = (Self, T);
fn make_full_output<T>(output: Self::Output, rest: T) -> Self::FullOutput<T> {
(output, rest)
}
fn default() -> Option<Self> {
<Self as Value>::default()
}
fn parse_from<S>(stream: &mut Peekable<S>) -> Result<Self::Output>
where
S: Iterator,
S::Item: AsRef<str> + Into<String>
{
stream.next()
.ok_or(Error::OutOfData)
.and_then(|s| <Self as Value>::parse(s.as_ref()))
}
}
)*};
}
impl_for!(String, f32, f64, bool, CString, Vec<Q: Parsable[Output = Q]>);
#[cfg(feature = "std")]
impl_for!(OsString, PathBuf);
trait Value: Sized {
fn default() -> Option<Self> {
None
}
fn parse(data: &str) -> Result<Self>;
}
impl Value for String {
fn parse(data: &str) -> Result<Self> {
Ok(data.to_string())
}
}
#[cfg(feature = "std")]
impl Value for OsString {
fn parse(data: &str) -> Result<Self> {
Ok(OsString::from(data))
}
}
#[cfg(feature = "std")]
impl Value for PathBuf {
fn parse(data: &str) -> Result<Self> {
Ok(PathBuf::from(data))
}
}
impl Value for CString {
fn parse(data: &str) -> Result<Self> {
CString::new(data).map_err(Error::other)
}
}
macro_rules! impl_for_int {
(signed = [$($signed:ident),*] unsigned = [$($unsigned:ident),*]) => {
$(
impl Value for $signed {
fn parse(data: &str) -> Result<Self> {
data.parse()
.map_err(|_| Error::InvalidSignedInteger(data.to_string()))
}
}
)*
$(
impl Value for $unsigned {
fn parse(data: &str) -> Result<Self> {
data.parse()
.map_err(|_| Error::InvalidUnsignedInteger(data.to_string()))
}
}
)*
impl_for!($($signed,)* $($unsigned),*);
};
}
impl_for_int!(
signed = [i8, i16, i32, i64, i128, isize]
unsigned = [u8, u16, u32, u64, u128, usize]
);
impl Value for f32 {
fn parse(data: &str) -> Result<Self> {
data.parse()
.map_err(|_| Error::InvalidFloat(data.to_string()))
}
}
impl Value for f64 {
fn parse(data: &str) -> Result<Self> {
data.parse()
.map_err(|_| Error::InvalidFloat(data.to_string()))
}
}
impl Value for bool {
fn default() -> Option<Self> {
Some(true)
}
fn parse(data: &str) -> Result<Self> {
Ok(match data {
"0" | "false" => false,
"1" | "true" => true,
_ => return Err(Error::InvalidBool(data.to_string())),
})
}
}
impl<T> Value for Vec<T>
where
T: Parsable<Output = T>,
{
fn parse(data: &str) -> Result<Self> {
let mut split = data.split(',').peekable();
let mut vals = Vec::new();
while split.peek().is_some() {
vals.push(T::parse_from(&mut split)?);
}
Ok(vals)
}
}
}
macro_rules! impl_tuples {
() => {};
(@impl) => {};
(@impl $($i:ident)*) => {
impl<$($i,)*> Parsable for ($($i,)*)
where
$($i: Parsable,)*
{
type Output = (
$(<$i as Parsable>::Output,)*
);
type FullOutput<T2> = (Self::Output, T2);
fn make_full_output<T2>(output: Self::Output, rest: T2) -> Self::FullOutput<T2> {
(output, rest)
}
fn default() -> Option<Self> {
Some((
$(<$i as Parsable>::default()?,)*
))
}
fn parse_from<S2>(stream: &mut Peekable<S2>) -> Result<Self::Output>
where
S2: Iterator,
S2::Item: AsRef<str> + Into<String>
{
Ok((
$(<$i as Parsable>::parse_from(stream)?,)*
))
}
}
};
($head:ident $($tail:ident)*) => {
impl_tuples!([$head] $($tail)*);
};
([$($curr:ident)*]) => {
impl_tuples!(@impl $($curr)*);
};
([$($curr:ident)*] $head:ident $($tail:ident)*) => {
impl_tuples!(@impl $($curr)*);
impl_tuples!([$($curr)* $head] $($tail)*);
};
}
impl_tuples!(A B C D E F G H I J K L M N O P Q R S T U V W X Y Z);