mavspec_rust_derive 0.6.7

Procedural macros for MAVSpec's Rust code generation toolchain.
Documentation
use quote::quote;

use crate::consts::{ATTR_CRC_EXTRA, ATTR_MESSAGE_ID, MESSAGE_ID_MAX};
use crate::errors::{CrcExtraError, Error, MessageIdError};

pub(crate) struct MessageId {
    literal: syn::LitInt,
    value: u32,
}

pub(crate) struct CrcExtra(syn::LitInt);

impl TryFrom<&Vec<syn::Attribute>> for MessageId {
    type Error = Error;

    fn try_from(value: &Vec<syn::Attribute>) -> Result<Self, Self::Error> {
        Self::try_from_attrs(value)
    }
}

impl TryFrom<&Vec<syn::Attribute>> for CrcExtra {
    type Error = Error;

    fn try_from(value: &Vec<syn::Attribute>) -> Result<Self, Self::Error> {
        Self::try_from_attrs(value)
    }
}

impl From<u8> for CrcExtra {
    fn from(value: u8) -> Self {
        Self(syn::LitInt::new(
            value.to_string().as_str(),
            proc_macro2::Span::call_site(),
        ))
    }
}

impl MessageId {
    pub(crate) fn try_from_attrs(attrs: &Vec<syn::Attribute>) -> Result<Self, Error> {
        let mut message_id: Option<MessageId> = None;

        for attr in attrs {
            if let Some(attr_ident) = attr.path().get_ident() {
                if attr_ident == ATTR_MESSAGE_ID {
                    match attr.parse_args().unwrap() {
                        syn::Expr::Lit(literal) => match literal.lit {
                            syn::Lit::Int(int_literal) => {
                                let value = match int_literal.base10_parse::<u32>() {
                                    Ok(value) => {
                                        if value > MESSAGE_ID_MAX {
                                            return Err(MessageIdError::OutOfBounds(value).into());
                                        }
                                        value
                                    }
                                    Err(err) => {
                                        return Err(MessageIdError::InvalidU32Literal(err).into())
                                    }
                                };

                                message_id = Some(MessageId {
                                    literal: int_literal,
                                    value,
                                });
                            }
                            _ => return Err(MessageIdError::NotAnInteger.into()),
                        },
                        _ => return Err(MessageIdError::NotALiteral.into()),
                    }
                }
            }
        }

        if message_id.is_none() {
            return Err(MessageIdError::MissingAttribute.into());
        }

        Ok(message_id.unwrap())
    }

    pub(crate) fn literal(&self) -> &syn::LitInt {
        &self.literal
    }

    pub(crate) fn supports_mavlink_1(&self) -> bool {
        self.value <= u8::MAX as u32
    }

    pub(crate) fn min_supported_mavlink_version(&self) -> proc_macro2::TokenStream {
        if self.supports_mavlink_1() {
            quote! {mavspec::rust::spec::MavLinkVersion::V1}
        } else {
            quote! {mavspec::rust::spec::MavLinkVersion::V2}
        }
    }
}

impl CrcExtra {
    pub(crate) fn try_from_attrs(attrs: &Vec<syn::Attribute>) -> Result<Self, Error> {
        let mut crc_extra: Option<CrcExtra> = None;

        for attr in attrs {
            if let Some(attr_ident) = attr.path().get_ident() {
                if attr_ident == ATTR_CRC_EXTRA {
                    match attr.parse_args().unwrap() {
                        syn::Expr::Lit(literal) => match literal.lit {
                            syn::Lit::Int(int_literal) => {
                                Self::validate_int_literal(&int_literal)?;

                                crc_extra = Some(CrcExtra(int_literal));
                            }
                            _ => return Err(CrcExtraError::NotAnInteger.into()),
                        },
                        _ => return Err(CrcExtraError::NotALiteral.into()),
                    }
                }
            }
        }

        if crc_extra.is_none() {
            return Err(CrcExtraError::MissingAttribute.into());
        }

        Ok(crc_extra.unwrap())
    }

    pub(crate) fn literal(&self) -> &syn::LitInt {
        &self.0
    }

    fn validate_int_literal(int_literal: &syn::LitInt) -> Result<(), Error> {
        match int_literal.base10_parse::<u8>() {
            Ok(value) => value,
            Err(err) => return Err(CrcExtraError::InvalidU8Literal(err).into()),
        };

        Ok(())
    }
}