cel-cxx-macros 0.2.5

Macros for the cel-cxx crate
Documentation
use crate::symbol::*;
use proc_macro2::TokenStream;
use quote::quote;

pub fn expand_derive_prost_value(input: syn::DeriveInput) -> syn::Result<TokenStream> {
    let ast = Ast::from_ast(&input)?;
    let tokens = [
        impl_typed_value(&ast),
        impl_into_value(&ast),
        impl_from_value(&ast),
    ]
    .into_iter()
    .collect();
    Ok(tokens)
}

struct Ast {
    ident: syn::Ident,
    cel_path: syn::Path,
    type_name_override: Option<syn::LitStr>,
}

impl Ast {
    fn from_ast(input: &syn::DeriveInput) -> syn::Result<Self> {
        if let Some(param) = input.generics.params.first() {
            return Err(syn::Error::new_spanned(
                param,
                "ProstValue cannot be derived for generic types",
            ));
        }

        let mut cel_path = None;
        let mut type_name_override = None;

        for attr in &input.attrs {
            if !attr.path().is_ident(CEL_CXX) {
                continue;
            }
            attr.parse_nested_meta(|meta| {
                if meta.path.is_ident(CRATE) {
                    let path: syn::Path = meta.value()?.parse()?;
                    cel_path = Some(path);
                } else if meta.path.is_ident(TYPE_NAME) {
                    let lit: syn::LitStr = meta.value()?.parse()?;
                    type_name_override = Some(lit);
                } else {
                    return Err(syn::Error::new_spanned(meta.path, "unknown attribute"));
                }
                Ok(())
            })?;
        }

        let cel_path =
            cel_path.unwrap_or_else(|| syn::parse_str(&format!("::{CEL_CXX}")).unwrap());

        Ok(Self {
            ident: input.ident.clone(),
            cel_path,
            type_name_override,
        })
    }

    fn type_name_expr(&self) -> TokenStream {
        if let Some(ref lit) = self.type_name_override {
            quote! { #lit.to_string() }
        } else {
            quote! { <Self as ::prost::Name>::full_name() }
        }
    }
}

fn impl_typed_value(ast: &Ast) -> TokenStream {
    let name = &ast.ident;
    let cel = &ast.cel_path;
    let type_name_expr = ast.type_name_expr();

    quote! {
        impl #cel::values::TypedValue for #name {
            fn value_type() -> #cel::types::ValueType {
                #cel::types::ValueType::Struct(#cel::types::StructType::new(#type_name_expr))
            }
        }
    }
}

fn impl_into_value(ast: &Ast) -> TokenStream {
    let name = &ast.ident;
    let cel = &ast.cel_path;
    let type_name_expr = ast.type_name_expr();

    quote! {
        impl #cel::values::IntoValue for #name {
            fn into_value(self) -> #cel::values::Value {
                use ::prost::Message as _;
                let type_name = #type_name_expr;
                let bytes = self.encode_to_vec();
                #cel::values::Value::Struct(
                    #cel::values::StructValue::from_bytes(type_name, bytes),
                )
            }
        }

        impl ::std::convert::From<#name> for #cel::values::Value {
            fn from(value: #name) -> Self {
                <#name as #cel::values::IntoValue>::into_value(value)
            }
        }
    }
}

fn impl_from_value(ast: &Ast) -> TokenStream {
    let name = &ast.ident;
    let cel = &ast.cel_path;

    quote! {
        impl #cel::values::FromValue for #name {
            type Output<'a> = #name;

            fn from_value<'a>(
                value: &'a #cel::values::Value,
            ) -> ::std::result::Result<Self::Output<'a>, #cel::values::FromValueError> {
                match value {
                    #cel::values::Value::Struct(sv) => {
                        use ::prost::Message as _;
                        <#name>::decode(sv.to_bytes()).map_err(|_| {
                            #cel::values::FromValueError::new_typed::<#name>(value.clone())
                        })
                    }
                    _ => Err(#cel::values::FromValueError::new_typed::<#name>(value.clone())),
                }
            }
        }

        impl ::std::convert::TryFrom<#cel::values::Value> for #name {
            type Error = #cel::values::FromValueError;
            fn try_from(value: #cel::values::Value) -> ::std::result::Result<Self, Self::Error> {
                <Self as #cel::values::FromValue>::from_value(&value)
            }
        }

        impl ::std::convert::TryFrom<&#cel::values::Value> for #name {
            type Error = #cel::values::FromValueError;
            fn try_from(value: &#cel::values::Value) -> ::std::result::Result<Self, Self::Error> {
                <Self as #cel::values::FromValue>::from_value(value)
            }
        }
    }
}