seedle-macros 1.8.2

cbor codegeneration
Documentation
use crate::cddl::Fields;
use crate::parse::Language;
use crate::print::prefix::Prefix;
use crate::print::utils::method_attrs;
use heck::*;
use proc_macro2::Span;
use proc_macro2::TokenStream;
use quote::ToTokens;
use quote::{format_ident, quote};
use std::borrow::Cow;
use syn::{Ident, LitStr};

pub struct VTable<'i> {
    pub structs: Vec<(&'i String, Cow<'i, Fields>)>,
    pub ident: &'i Ident,
    pub language: Language,
    pub prefix: Option<&'i LitStr>,
}
impl<'i> ToTokens for VTable<'i> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let lang = self.language;
        let span = Span::call_site();
        let prefix = self
            .prefix
            .map(|v| Prefix::new(v.value()))
            .unwrap_or_else(|| Prefix::new(""));
        let n = proc_macro2::Literal::usize_unsuffixed(self.structs.len());
        let (key, table) = match self.language {
            Language::C => {
                let key = Ident::new(&format!("{prefix}KEY").to_shouty_snake_case(), span);
                let entries = self.structs.iter().map(|(key, _)| VTableEntry {
                    entry: Ident::new(&key.to_shouty_snake_case(), span),
                    language: lang,
                    prefix: &prefix,
                });
                let enumeration = self.structs.iter().enumerate().map(|(idx, (key, _node))| {
                    let name = Ident::new(&format!("{prefix}{key}").to_shouty_snake_case(), span);
                    let idx = proc_macro2::Literal::i8_unsuffixed(idx as i8);
                    quote! {#name = #idx}
                });
                let table = quote! {
                    const __SEEDLE_VTABLE: [seedle::ffi::SeedleVTableEntry; #n] = [ #(#entries),* ];

                    #[repr(C)]
                    pub enum #key {
                        #(#enumeration),*
                    }
                };
                (key, table)
            }
            Language::Rust => {
                let key = format_ident!("SeedleKey");
                let entries = self.structs.iter().map(|(key, _)| VTableEntry {
                    entry: Ident::new(&key.to_upper_camel_case(), span),
                    language: lang,
                    prefix: &prefix,
                });
                let enumeration = self.structs.iter().enumerate().map(|(idx, (key, _node))| {
                    let name = Ident::new(&key.to_upper_camel_case(), span);
                    let idx = proc_macro2::Literal::i8_unsuffixed(idx as i8);
                    quote! {#name = #idx}
                });
                let table = quote! {
                    const __SEEDLE_VTABLE: [seedle::ffi::SeedleVTableEntry; #n] = [ #(#entries),* ];

                    #[repr(C)]
                    pub enum #key {
                        #(#enumeration),*
                    }
                };
                (key, table)
            }
            Language::Typescript => {
                let key = format_ident!("SeedleKey");
                let entries = self.structs.iter().map(|(key, _)| VTableEntry {
                    entry: Ident::new(&key.to_upper_camel_case(), span),
                    language: lang,
                    prefix: &prefix,
                });
                let enumeration = self.structs.iter().enumerate().map(|(idx, (key, _node))| {
                    let name = Ident::new(&key.to_upper_camel_case(), span);
                    let idx = proc_macro2::Literal::i8_unsuffixed(idx as i8);
                    quote! {#name = #idx}
                });
                let table = quote! {
                    const __SEEDLE_VTABLE: [seedle::ffi::SeedleVTableEntry; #n] = [ #(#entries),* ];

                    #[wasm_bindgen]
                    #[repr(C)]
                    pub enum #key {
                        #(#enumeration),*
                    }
                };
                (key, table)
            }
        };
        let (enc, enc_attrs) = method_attrs(lang, format_ident!("{prefix}encode"));
        let (enc_arr, enc_arr_attrs) = method_attrs(lang, format_ident!("{prefix}encode_array"));
        let (dec, dec_attrs) = method_attrs(lang, format_ident!("{prefix}decode"));
        let (dec_arr, dec_arr_attrs) = method_attrs(lang, format_ident!("{prefix}decode_array"));
        let (len, len_attrs) = method_attrs(lang, format_ident!("{prefix}len"));
        let (len_arr, len_arr_attrs) = method_attrs(lang, format_ident!("{prefix}array_len"));

        quote! {
            #table

            #enc_attrs
            fn #enc(dst: *mut u8, dstlen: u32, key: #key, src: *const core::ffi::c_void) -> i32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].encode)(dst, dstlen, &*src)}
            }

            #enc_arr_attrs
            fn #enc_arr(dst: *mut u8, dstlen: u32, key: #key, src: *const core::ffi::c_void, srclen: u32) -> i32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].encode_array)(dst, dstlen, &*src, srclen)}
            }

            #dec_attrs
            fn #dec (dst: *mut core::ffi::c_void, key: #key, src: *const u8, srclen: u32) -> i32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].decode)(&mut *dst, src, srclen)}
            }

            #dec_arr_attrs
            fn #dec_arr (dst: *mut core::ffi::c_void, dstlen: u32, key: #key, src: *const u8, srclen: u32) -> i32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].decode_array)(&mut *dst, dstlen, src, srclen)}
            }

            #len_attrs
            fn #len(key: #key, src: *const core::ffi::c_void) -> u32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].len)(&*src)}
            }

            #len_arr_attrs
            fn #len_arr(key: #key, src: *const core::ffi::c_void, srclen: u32) -> u32 {
                unsafe {(__SEEDLE_VTABLE[key as u8 as usize].array_len)(&*src, srclen)}
            }
        }
        .to_tokens(tokens);
    }
}

struct VTableEntry<'l> {
    entry: Ident,
    language: Language,
    prefix: &'l Prefix,
}
impl<'l> ToTokens for VTableEntry<'l> {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let name = self.entry.to_string().to_snake_case();
        let prefix = self.prefix;
        let span = Span::call_site();
        let struct_name = match self.language {
            Language::C => Ident::new(&format!("{prefix}{name}").to_snake_case(), span),
            _ => Ident::new(&format!("{prefix}{name}").to_upper_camel_case(), span),
        };
        let encoder = quote::format_ident!("{prefix}encode_{name}");
        let decoder = quote::format_ident!("{prefix}decode_{name}");
        let array_encoder = quote::format_ident!("{prefix}encode_{name}_array");
        let array_decoder = quote::format_ident!("{prefix}decode_{name}_array");
        let len = quote::format_ident!("{prefix}len_{name}");
        let array_len = quote::format_ident!("{prefix}array_len_{name}");
        quote! {
            unsafe { seedle::ffi::SeedleVTableEntry {
                encode: core::mem::transmute::<seedle::ffi::EncodeFn<#struct_name>, seedle::ffi::EncodeFnErased>(#encoder),
                encode_array: core::mem::transmute::<seedle::ffi::EncodeArrayFn<#struct_name>, seedle::ffi::EncodeArrayFnErased>(#array_encoder),
                decode: core::mem::transmute::<seedle::ffi::DecodeFn<#struct_name>, seedle::ffi::DecodeFnErased>(#decoder),
                decode_array: core::mem::transmute::<seedle::ffi::DecodeArrayFn<#struct_name>, seedle::ffi::DecodeArrayFnErased>(#array_decoder),
                len: core::mem::transmute::<seedle::ffi::LenFn<#struct_name>, seedle::ffi::LenFnErased>(#len),
                array_len: core::mem::transmute::<seedle::ffi::ArrayLenFn<#struct_name>, seedle::ffi::ArrayLenFnErased>(#array_len),
            }}
        }
        .to_tokens(tokens)
    }
}