obake_macros 1.0.4

Macros for versioned data-structures
Documentation
use std::convert::{TryFrom, TryInto};

use syn::parse::{Parse, ParseStream, Result};
use syn::{braced, parenthesized, Token};

use crate::internal::*;

const OBAKE: &str = "obake";

impl Parse for VersionAttr {
    fn parse(input: ParseStream) -> Result<Self> {
        let version_str = input.parse::<syn::LitStr>()?;
        let span = version_str.span();
        let version = Version::parse(&version_str.value())
            .map_err(|err| syn::Error::new(version_str.span(), err))?;

        Ok(Self { version, span })
    }
}

impl Parse for CfgAttr {
    fn parse(input: ParseStream) -> Result<Self> {
        let req_str = input.parse::<syn::LitStr>()?;
        let span = req_str.span();
        let req = VersionReq::parse(&req_str.value())
            .map_err(|err| syn::Error::new(req_str.span(), err))?;

        Ok(Self { req, span })
    }
}

impl Parse for ObakeAttribute {
    fn parse(input: ParseStream) -> Result<Self> {
        let ident = input.parse::<syn::Ident>()?;

        Ok(match ident {
            _ if ident == "version" => {
                let content;
                parenthesized!(content in input);
                Self::Version(content.parse()?)
            }
            _ if ident == "cfg" => {
                let content;
                parenthesized!(content in input);
                Self::Cfg(content.parse()?)
            }
            _ if ident == "inherit" => Self::Inherit(InheritAttr { span: ident.span() }),
            _ if ident == "derive" => {
                let content;
                parenthesized!(content in input);
                Self::Derive(DeriveAttr {
                    span: ident.span(),
                    tokens: content.parse()?,
                })
            }
            #[cfg(feature = "serde")]
            _ if ident == "serde" => {
                let content;
                parenthesized!(content in input);
                Self::Serde(SerdeAttr {
                    span: ident.span(),
                    tokens: content.parse()?,
                })
            }
            _ => {
                return Err(syn::Error::new(
                    ident.span(),
                    "unrecognised `obake` helper attribute",
                ))
            }
        })
    }
}

impl TryFrom<syn::Attribute> for ObakeAttribute {
    type Error = syn::Error;

    fn try_from(attr: syn::Attribute) -> Result<Self> {
        attr.parse_args()
    }
}

impl TryFrom<syn::Attribute> for VersionedAttribute {
    type Error = syn::Error;

    fn try_from(attr: syn::Attribute) -> Result<Self> {
        attr.path.get_ident().map_or_else(
            || Ok(Self::Attribute(attr.clone())),
            |ident| {
                if ident == OBAKE {
                    Ok(Self::Obake(attr.clone().try_into()?))
                } else {
                    Ok(Self::Attribute(attr.clone()))
                }
            },
        )
    }
}

impl Parse for VersionedAttributes {
    fn parse(input: ParseStream) -> Result<VersionedAttributes> {
        let attrs = input
            .call(syn::Attribute::parse_outer)?
            .into_iter()
            .map(TryInto::try_into)
            .collect::<Result<Vec<_>>>()?;

        Ok(Self { attrs })
    }
}

impl Parse for VersionedField {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            attrs: input.parse()?,
            vis: input.parse()?,
            ident: input.parse()?,
            colon_token: input.parse()?,
            ty: input.parse()?,
        })
    }
}

impl Parse for VersionedFields {
    fn parse(input: ParseStream) -> Result<Self> {
        let content;
        let brace_token = braced!(content in input);

        Ok(Self {
            brace_token,
            fields: content.parse_terminated(VersionedField::parse)?,
        })
    }
}

impl Parse for VersionedVariantFields {
    fn parse(input: ParseStream) -> Result<Self> {
        if input.is_empty() {
            return Ok(Self::Unit);
        }

        let lookahead = input.lookahead1();
        Ok(if lookahead.peek(syn::token::Paren) {
            Self::Unnamed(input.parse()?)
        } else if lookahead.peek(syn::token::Brace) {
            Self::Named(input.parse()?)
        } else {
            Self::Unit
        })
    }
}

impl Parse for VersionedVariant {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            attrs: input.parse()?,
            ident: input.parse()?,
            fields: input.parse()?,
        })
    }
}

impl Parse for VersionedVariants {
    fn parse(input: ParseStream) -> Result<Self> {
        let content;
        let brace_token = braced!(content in input);

        Ok(Self {
            brace_token,
            variants: content.parse_terminated(VersionedVariant::parse)?,
        })
    }
}

impl Parse for VersionedStruct {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            struct_token: input.parse()?,
            ident: input.parse()?,
            fields: input.parse()?,
        })
    }
}

impl Parse for VersionedEnum {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            enum_token: input.parse()?,
            ident: input.parse()?,
            variants: input.parse()?,
        })
    }
}

impl Parse for VersionedItemKind {
    fn parse(input: ParseStream) -> Result<Self> {
        let lookahead = input.lookahead1();
        if lookahead.peek(Token![struct]) {
            Ok(Self::Struct(input.parse()?))
        } else if lookahead.peek(Token![enum]) {
            Ok(Self::Enum(input.parse()?))
        } else {
            Err(lookahead.error())
        }
    }
}

impl Parse for VersionedItem {
    fn parse(input: ParseStream) -> Result<Self> {
        Ok(Self {
            attrs: input.parse()?,
            vis: input.parse()?,
            kind: input.parse()?,
        })
    }
}