timesource-derive 0.1.3

Macros for Timesource crate
Documentation
use proc_macro2::TokenStream;
use syn::{Attribute, Error, Lit};

use crate::encoding::PayloadEncoding;

pub struct EventMeta {
    pub encoding: PayloadEncoding,
    pub version: Option<String>,
}

impl Default for EventMeta {
    fn default() -> Self {
        Self {
            encoding: PayloadEncoding::Json,
            version: None,
        }
    }
}

impl TryFrom<Option<&Attribute>> for EventMeta {
    type Error = TokenStream;

    fn try_from(attr: Option<&Attribute>) -> Result<Self, Self::Error> {
        let mut event_attrs = EventMeta::default();

        if let Some(attr) = attr {
            let meta = attr.parse_meta().unwrap();

            match meta {
                syn::Meta::List(list) => {
                    for nested in list.nested {
                        match nested {
                            syn::NestedMeta::Meta(meta) => match meta {
                                syn::Meta::NameValue(nv) => {
                                    if nv.path.is_ident("encoding") {
                                        if let Lit::Str(lit) = nv.lit {
                                            event_attrs.encoding =
                                                PayloadEncoding::try_from(lit.value().as_ref())
                                                    .unwrap();
                                        }
                                    } else if nv.path.is_ident("version") {
                                        if let Lit::Str(lit) = nv.lit {
                                            event_attrs.version = Some(lit.value());
                                        }
                                    }
                                }
                                meta_type => {
                                    return Err(Error::new_spanned(
                                        meta_type,
                                        "Unexpected meta type",
                                    )
                                    .to_compile_error());
                                }
                            },
                            meta_type => {
                                return Err(Error::new_spanned(
                                    meta_type,
                                    "Unexpected nested meta type",
                                )
                                .to_compile_error());
                            }
                        }
                    }
                }
                meta_type => {
                    return Err(
                        Error::new_spanned(meta_type, "Unexpected meta type").to_compile_error()
                    )
                }
            }
        }

        Ok(event_attrs)
    }
}

impl quote::ToTokens for EventMeta {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let version_token = match self.version.clone() {
            Some(version) => quote!(std::option::Option::Some(#version)),
            None => quote!(std::option::Option::None),
        };

        let payload_kind_token = match self.encoding {
            PayloadEncoding::Json => quote!(timesource::PayloadEncoding::Json),
            PayloadEncoding::Cbor => quote!(timesource::PayloadEncoding::Cbor),
            PayloadEncoding::ProtoBuf => quote!(timesource::PayloadEncoding::ProtoBuf),
        };

        let mut encoding_token = TokenStream::new();
        self.encoding.to_tokens(&mut encoding_token);

        tokens.extend(quote! {
            fn version() -> Option<&'static str> {
                #version_token
            }

            fn encoding() -> timesource::PayloadEncoding {
                #payload_kind_token
            }

            #encoding_token
        });
    }
}