extern crate self as fcla;
pub use fcla_macros::FromArgs;
mod array;
mod c_like;
mod char;
mod coerced;
mod future;
mod iter;
mod map;
mod net;
pub mod num;
mod path;
mod phantom;
mod range;
mod sequence;
mod string;
mod sync;
mod tuple;
mod wrapper;
pub use map::*;
pub use range::*;
#[allow(unused_imports)] use std::str::FromStr;
use std::{error::Error, ffi::OsString, fmt};
pub type MainResult<E> = Result<(), MainError<E>>;
pub type ParseResult<T> = Result<T, ParseError>;
pub type FromArgsResult<T> = Result<T, FromArgsError>;
type Branch<S, T> = (&'static str, Parser<S, T>);
type Parser<S, T> = fn(args: &mut Args<S>) -> FromArgsResult<T>;
pub fn parse_cla<T: FromArgs>() -> Result<Cla<T>, ClaError> {
parse_env().map_err(ClaError)
}
pub fn parse_env<T: FromArgs>() -> ParseResult<T> {
parse(std::env::args_os())
}
pub fn parse<I, T>(args: I) -> ParseResult<T>
where
I: IntoIterator<Item = OsString>,
I::IntoIter: ExactSizeIterator,
T: FromArgs,
{
let source = args.into_iter();
let mut args = Args(source);
let value = from_args(&mut args).map_err(ParseError::FromArgs)?;
let Args(source) = args;
if source.len() > 0 {
return Err(ParseError::TooManyArguments);
}
Ok(value)
}
pub fn from_args<T, S>(args: &mut Args<S>) -> FromArgsResult<T>
where
T: FromArgs,
S: Source + ?Sized,
{
T::from_args(args)
}
pub trait FromArgs: Sized {
fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self>;
}
pub trait Source: Iterator<Item = OsString> {}
impl<I: Iterator<Item = OsString>> Source for I {}
pub trait FromArg: Sized {
type Parent: FromArgs;
type Error;
fn from_arg(arg: Self::Parent) -> Result<Self, Self::Error>;
fn box_error(error: Self::Error) -> Box<dyn Error + Send + Sync>;
}
impl<T: FromArg> FromArgs for T {
fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
let parent = from_args(args)?;
T::from_arg(parent).map_err(|error| FromArgsError::new(Self::box_error(error)))
}
}
#[doc(hidden)]
pub struct Arg(Option<OsString>);
impl FromArgs for Arg {
fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
let Args(args) = args;
Ok(Arg(args.next()))
}
}
impl FromArg for OsString {
type Parent = Arg;
type Error = ();
fn from_arg(arg: Self::Parent) -> Result<Self, Self::Error> {
let Arg(arg) = arg;
arg.ok_or(())
}
fn box_error((): Self::Error) -> Box<dyn Error + Send + Sync> {
Box::new(MissingArgumentError)
}
}
impl<T: FromArgs> FromArgs for Option<T> {
fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
let Args(inner) = args;
match inner.next().as_ref().and_then(|arg| arg.to_str()) {
Some(".") => Ok(None),
Some(",") => Ok(Some(from_args(args)?)),
_ => Err(FromArgsError::new(MissingSeparator)),
}
}
}
#[derive(Debug, Clone)]
pub struct Args<S: ?Sized>(S);
impl<S: Source + ?Sized> Args<S> {
pub fn parse<T: FromArgs>(&mut self, field: Ident) -> FromArgsResult<T> {
from_args(self).map_err(|error| error.add_context(Context::Field(field)))
}
pub fn branch<T, const LEN: usize>(
&mut self,
branches: [Branch<S, T>; LEN],
) -> FromArgsResult<T> {
let variant = OsString::from_args(self)?;
branches
.iter()
.find_map(|&(ident, parser)| {
(variant == ident).then(|| {
parser(self).map_err(|error| error.add_context(Context::Variant(ident)))
})
})
.ok_or(FromArgsError::new(UnmatchedVariantError))?
}
pub fn as_iter<T: FromArgs>(&mut self) -> AsIter<'_, S, T> {
AsIter {
args: self,
parser: Option::<T>::from_args,
index: 0,
}
}
}
pub struct AsIter<'a, S: ?Sized, T> {
args: &'a mut Args<S>,
parser: Parser<S, Option<T>>,
index: usize,
}
impl<'a, S: Source + ?Sized, T> Iterator for AsIter<'a, S, T> {
type Item = FromArgsResult<T>;
fn next(&mut self) -> Option<Self::Item> {
let Self {
ref mut args,
parser,
index,
} = *self;
self.index += 1;
parser(args)
.map_err(|error| error.add_context(Context::Field(Ident::Unnamed(index))))
.transpose()
}
}
#[derive(Debug)]
pub enum ParseError {
TooManyArguments,
FromArgs(FromArgsError),
}
#[derive(Debug)]
pub struct FromArgsError {
cause: Box<dyn Error + Send + Sync>,
trace: Vec<Context>,
}
#[derive(Debug)]
enum Context {
Field(Ident),
Variant(&'static str),
}
#[derive(Debug, Clone, Copy)]
pub enum Ident {
Unnamed(usize),
Named(&'static str),
}
impl FromArgsError {
fn new(cause: impl Into<Box<dyn Error + Send + Sync>>) -> Self {
Self {
cause: cause.into(),
trace: Vec::new(),
}
}
fn add_context(mut self, context: Context) -> Self {
self.trace.push(context);
self
}
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::TooManyArguments => write!(f, "too many arguments"),
Self::FromArgs(error) => error.fmt(f),
}
}
}
impl fmt::Display for FromArgsError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { cause, trace } = self;
let trace = TraceDisplay { trace };
write!(f, "{trace}: {cause}")
}
}
struct TraceDisplay<'a> {
trace: &'a [Context],
}
impl<'a> fmt::Display for TraceDisplay<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.trace
.iter()
.rev()
.try_for_each(|context| context.fmt(f))
}
}
impl fmt::Display for Context {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Context::*;
match self {
Field(ident) => write!(f, ".{ident}"),
Variant(ident) => write!(f, "::{ident}"),
}
}
}
impl fmt::Display for Ident {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Ident::*;
match self {
Unnamed(index) => index.fmt(f),
Named(ident) => ident.fmt(f),
}
}
}
impl Error for ParseError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
Self::TooManyArguments => None,
Self::FromArgs(error) => error.source(),
}
}
}
impl Error for FromArgsError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.cause.source()
}
}
#[derive(Debug)]
struct MissingArgumentError;
impl fmt::Display for MissingArgumentError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "argument missing")
}
}
impl Error for MissingArgumentError {}
#[derive(Debug)]
struct MissingSeparator;
impl fmt::Display for MissingSeparator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "separator missing")
}
}
impl Error for MissingSeparator {}
#[derive(Debug)]
struct UnmatchedVariantError;
impl fmt::Display for UnmatchedVariantError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "argument doesn't match any of the variants")
}
}
impl Error for UnmatchedVariantError {}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct TypeError(pub &'static str);
impl fmt::Display for TypeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self(ty) = self;
write!(f, "argument is not a valid {ty}")
}
}
impl Error for TypeError {}
#[derive(Debug, Clone)]
pub struct Cla<T> {
pub path: Box<std::path::Path>,
pub args: T,
}
impl<T: FromArgs> FromArgs for Cla<T> {
fn from_args<S: Source + ?Sized>(args: &mut Args<S>) -> FromArgsResult<Self> {
let path = args.parse(Ident::Named("self"))?;
let args = from_args(args)?;
Ok(Self { path, args })
}
}
pub struct ClaError(ParseError);
pub enum MainError<E> {
Argument(ParseError),
Runtime(E),
}
impl<E: fmt::Debug> fmt::Debug for MainError<E> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Argument(error) => write!(f, "parsing command line arguments: {error}"),
Self::Runtime(error) => error.fmt(f),
}
}
}
impl<E> From<ClaError> for MainError<E> {
fn from(ClaError(inner): ClaError) -> Self {
Self::Argument(inner)
}
}
impl<E: fmt::Debug + Into<Box<E>>> From<E> for MainError<E> {
fn from(value: E) -> Self {
Self::Runtime(value)
}
}
mod internal {
pub use super::{from_args, Args, FromArg, FromArgs, FromArgsResult, Ident, Source, TypeError};
pub use std::convert::Infallible;
pub use std::error::Error;
pub use std::fmt;
#[cfg(test)]
pub use super::ParseResult;
#[cfg(test)]
pub fn parse<A, T, const LEN: usize>(args: [A; LEN]) -> ParseResult<T>
where
A: ToString,
T: FromArgs,
{
let args = args.iter().map(ToString::to_string).map(Into::into);
super::parse(args)
}
}
pub mod prelude {
pub use super::{Args, FromArg, FromArgs, FromArgsResult, Source};
}