throwing-macros 0.1.1

Create explicit errors easily with a handy macro
Documentation
use syn::{
    parse::{Parse, ParseStream},
    punctuated::Punctuated,
    Ident, Result, Token, Type, Visibility,
};

pub enum VariantArg {
    Variant { typ: Type, name: Option<Ident> },
    Composed { typ: Type },
}

impl Parse for VariantArg {
    fn parse(input: ParseStream) -> Result<Self> {
        let break_tok: Option<Token!(break)> = input.parse()?;

        if break_tok.is_some() {
            let typ: Type = input.parse()?;
            Ok(VariantArg::Composed { typ })
        } else {
            let typ: Type = input.parse()?;
            let as_tok: Option<Token!(as)> = input.parse()?;

            let name: Option<Ident> = if as_tok.is_some() {
                Some(input.parse()?)
            } else {
                None
            };

            Ok(VariantArg::Variant { typ, name })
        }
    }
}

pub struct TypeDef {
    pub visibility: Visibility,
    pub name: Ident,
}

impl Parse for TypeDef {
    fn parse(input: ParseStream) -> Result<Self> {
        let visibility = input.parse()?;
        input.parse::<Token!(type)>()?;
        let name = input.parse()?;

        Ok(TypeDef { visibility, name })
    }
}

pub type VariantArgs = Punctuated<VariantArg, Token!(|)>;

pub struct DefineErrorArgs {
    pub type_def: TypeDef,
    pub variants: VariantArgs,
}

impl Parse for DefineErrorArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let type_def = input.parse()?;
        let equal: Option<Token!(=)> = input.parse()?;

        let variants = if equal.is_some() {
            Punctuated::parse_terminated(input)?
        } else {
            VariantArgs::default()
        };

        Ok(DefineErrorArgs { type_def, variants })
    }
}

pub struct ThrowsArgs {
    pub name: Option<Ident>,
    pub variants: VariantArgs,
}

impl Parse for ThrowsArgs {
    fn parse(input: ParseStream) -> Result<Self> {
        let type_tok: Option<Token!(type)> = input.parse()?;

        if type_tok.is_some() {
            let name = input.parse()?;

            let equal: Option<Token!(=)> = input.parse()?;

            let variants = if equal.is_some() {
                Punctuated::parse_terminated(input)?
            } else {
                VariantArgs::default()
            };

            Ok(ThrowsArgs {
                name: Some(name),
                variants,
            })
        } else {
            Ok(ThrowsArgs {
                name: None,
                variants: Punctuated::parse_terminated(input)?,
            })
        }
    }
}