brec_macros 0.4.0

Code generator for brec. Implements the block and payload macros.
Documentation
use crate::*;
use proc_macro2::TokenStream;
use quote::quote;

impl Base for Payload {
    fn generate(&self) -> Result<TokenStream, E> {
        let payload_name = self.name();
        let sig = self.sig()?;
        let sig_impl = if self.attrs.no_default_sig() {
            quote! {}
        } else {
            quote! {

                impl brec::PayloadSignature for #payload_name {
                    fn sig(&self) -> brec::ByteBlock {
                        brec::ByteBlock::Len4(#sig)
                    }
                }
                impl brec::StaticPayloadSignature for #payload_name {
                    fn ssig() -> brec::ByteBlock {
                        brec::ByteBlock::Len4(#sig)
                    }
                }

            }
        };

        let hooks_impl = if self.attrs.hooks() {
            quote! {}
        } else {
            quote! {
                impl brec::PayloadHooks for #payload_name { }
            }
        };
        let schema_impl = quote! {
            impl brec::PayloadSchema for #payload_name {
                type Context<'a> = crate::PayloadContext<'a>;
            }
        };
        let bincode_impl = if self.attrs.is_bincode() && !self.attrs.is_crypt() {
            quote! {
                impl brec::PayloadEncode for #payload_name {
                    fn encode(&self, _ctx: &mut Self::Context<'_>) -> std::io::Result<Vec<u8>> {
                        brec::bincode::serde::encode_to_vec(self, brec::bincode::config::standard())
                            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))
                    }
                }

                impl brec::PayloadEncodeReferred for #payload_name {
                    fn encode(&self, _ctx: &mut Self::Context<'_>) -> std::io::Result<Option<&[u8]>> {
                        Ok(None)
                    }
                }

                impl brec::PayloadDecode<#payload_name> for #payload_name {
                    fn decode(buf: &[u8], _ctx: &mut Self::Context<'_>) -> std::io::Result<#payload_name> {
                        brec::bincode::serde::decode_from_slice(buf, brec::bincode::config::standard())
                            .map(|(value, _)| value)
                            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))
                    }
                }
            }
        } else {
            quote! {}
        };
        let crypt_and_bincode_impl = if self.attrs.is_crypt() && self.attrs.is_bincode() {
            quote! {
                impl brec::PayloadEncode for #payload_name {
                    fn encode(&self, ctx: &mut Self::Context<'_>) -> std::io::Result<Vec<u8>> {
                        let payload_body = brec::bincode::serde::encode_to_vec(self, brec::bincode::config::standard())
                            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
                        let encrypt_options = match ctx {
                            crate::PayloadContext::Encrypt(opt) => opt,
                            _ => {
                                return Err(std::io::Error::new(
                                    std::io::ErrorKind::InvalidInput,
                                    format!(
                                        "payload {} with #[payload(crypt, bincode)] expects PayloadContext::Encrypt",
                                        stringify!(#payload_name),
                                    ),
                                ));
                            }
                        };
                        brec::BricCryptCodec::encrypt(&payload_body, encrypt_options).map_err(std::io::Error::from)
                    }
                }

                impl brec::PayloadEncodeReferred for #payload_name {
                    fn encode(&self, _ctx: &mut Self::Context<'_>) -> std::io::Result<Option<&[u8]>> {
                        Ok(None)
                    }
                }

                impl brec::PayloadDecode<#payload_name> for #payload_name {
                    fn decode(buf: &[u8], ctx: &mut Self::Context<'_>) -> std::io::Result<#payload_name> {
                        let decrypt_options = match ctx {
                            crate::PayloadContext::Decrypt(opt) => opt,
                            _ => {
                                return Err(std::io::Error::new(
                                    std::io::ErrorKind::InvalidInput,
                                    format!(
                                        "payload {} with #[payload(crypt, bincode)] expects PayloadContext::Decrypt",
                                        stringify!(#payload_name),
                                    ),
                                ));
                            }
                        };
                        let payload_body = brec::BricCryptCodec::decrypt(buf, decrypt_options)
                            .map_err(std::io::Error::from)?;
                        brec::bincode::serde::decode_from_slice(&payload_body, brec::bincode::config::standard())
                            .map(|(value, _)| value)
                            .map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))
                    }
                }
            }
        } else {
            quote! {}
        };
        let napi_impl = {
            #[cfg(feature = "napi")]
            {
                brec_node_gen::codegen::base::payload::generate(&self.name(), &self.attrs)?
            }
            #[cfg(not(feature = "napi"))]
            {
                quote! {}
            }
        };
        let wasm_impl = {
            #[cfg(feature = "wasm")]
            {
                brec_wasm_gen::codegen::base::payload::generate(&self.name(), &self.attrs)?
            }
            #[cfg(not(feature = "wasm"))]
            {
                quote! {}
            }
        };
        let java_impl = {
            #[cfg(feature = "java")]
            {
                brec_java_gen::codegen::base::payload::generate(&self.name(), &self.attrs)?
            }
            #[cfg(not(feature = "java"))]
            {
                quote! {}
            }
        };
        let csharp_impl = {
            #[cfg(feature = "csharp")]
            {
                brec_csharp_gen::codegen::base::payload::generate(&self.name(), &self.attrs)?
            }
            #[cfg(not(feature = "csharp"))]
            {
                quote! {}
            }
        };
        Ok(quote! {
            #sig_impl
            #hooks_impl
            #schema_impl
            #bincode_impl
            #crypt_and_bincode_impl
            #napi_impl
            #wasm_impl
            #java_impl
            #csharp_impl
        })
    }
}

impl Gen for Payload {
    fn generate(&self) -> Result<TokenStream, E> {
        let base = Base::generate(self)?;
        let read = Read::generate(self)?;
        let try_read = TryRead::generate(self)?;
        let try_read_buffered = TryReadBuffered::generate(self)?;
        let crc = Crc::generate(self)?;
        let size = Size::generate(self);
        let write = Write::generate(self)?;
        let write_vec = WriteVectored::generate(self)?;
        Ok(quote! {
            #base
            #crc
            #size
            #read
            #try_read
            #try_read_buffered
            #write
            #write_vec
        })
    }
}