maybe-fatal-derive 0.1.0-beta.4

Code generation for the maybe-fatal crate
Documentation
use syn::{Attribute, LitStr, Token, meta::ParseNestedMeta};

#[derive(Clone, Default)]
pub struct MaybeFatal {
    pub span_type: Option<syn::Type>,
    pub group: Option<GroupMeta>,
}

impl MaybeFatal {
    pub fn parse_from_attrs<'a>(
        attrs: impl IntoIterator<Item = &'a Attribute>,
    ) -> syn::Result<Self> {
        let mut this = Self::default();
        for attr in attrs {
            this.try_extend_with(attr)?;
        }

        Ok(this)
    }

    pub fn try_extend_with(&mut self, attr: &Attribute) -> syn::Result<()> {
        if attr.path().is_ident("maybe_fatal") {
            attr.parse_nested_meta(|meta| self.parse_nested_subattribute(meta))
        } else {
            Ok(())
        }
    }

    fn parse_nested_subattribute(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if meta.path.is_ident("span_type") {
            self.parse_span_type(meta)
        } else if meta.path.is_ident("group") {
            self.parse_group(meta)
        } else {
            Err(meta.error("unrecognized meta attribute"))
        }
    }

    fn parse_span_type(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if self.span_type.is_some() {
            return Err(meta.error("repeated `span_type` meta attribute"));
        }

        let _: Token![=] = meta.input.parse()?;
        self.span_type = Some(meta.input.parse()?);

        Ok(())
    }

    fn parse_group(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if self.group.is_some() {
            return Err(meta.error("repeated `group` meta attribute"));
        }

        self.group = Some(GroupMeta::parse_from_nested_meta(meta)?);

        Ok(())
    }
}

#[derive(Clone, Default)]
pub struct GroupMeta {
    pub prefix: Option<[u8; 3]>,
    pub discriminant_type: Option<syn::Type>,
}

impl GroupMeta {
    pub fn parse_from_nested_meta(meta: ParseNestedMeta<'_>) -> syn::Result<Self> {
        let mut this = Self::default();
        meta.parse_nested_meta(|meta| this.parse_nested_subattribute(meta))?;

        Ok(this)
    }

    fn parse_nested_subattribute(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if meta.path.is_ident("prefix") {
            self.parse_prefix(meta)
        } else if meta.path.is_ident("discriminant_type") {
            self.parse_discriminant_type(meta)
        } else {
            Err(meta.error("unrecognized meta attribute"))
        }
    }

    fn parse_prefix(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if self.prefix.is_some() {
            return Err(meta.error("repeated `prefix` meta attribute"));
        }

        let _: Token![=] = meta.input.parse()?;
        let lit_prefix: LitStr = meta.input.parse()?;
        self.prefix = Some(lit_prefix.value().into_bytes().try_into().map_err(|_| {
            syn::Error::new(lit_prefix.span(), "prefix is incorrect length (expected 3)")
        })?);

        Ok(())
    }

    fn parse_discriminant_type(&mut self, meta: ParseNestedMeta<'_>) -> syn::Result<()> {
        if self.discriminant_type.is_some() {
            return Err(meta.error("repeated `discriminant_type` meta attribute"));
        }

        let _: Token![=] = meta.input.parse()?;
        self.discriminant_type = Some(meta.input.parse()?);

        Ok(())
    }
}