clicktype-macros 0.2.0

Procedural macros for ClickType - #[derive(ClickTable)] and more
Documentation
//! Implementation of #[derive(ClickEnum)] macro

use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Fields};

pub fn derive(input: TokenStream) -> TokenStream {
    let input = parse_macro_input!(input as DeriveInput);
    let enum_name = &input.ident;

    // Only support unit enums
    let variants = match &input.data {
        Data::Enum(data) => &data.variants,
        _ => panic!("#[derive(ClickEnum)] only supports enums"),
    };

    // Collect variant names and ensure they're all unit variants
    let mut variant_names = Vec::new();
    for variant in variants {
        match &variant.fields {
            Fields::Unit => {
                variant_names.push(&variant.ident);
            }
            _ => panic!("#[derive(ClickEnum)] only supports unit variants (variants without fields)"),
        }
    }

    // Generate discriminant values (0, 1, 2, ...)
    let discriminants: Vec<i8> = (0..variant_names.len() as i8).collect();

    let expanded = quote! {
        impl clicktype_core::traits::ClickHouseType for #enum_name {
            fn type_name() -> std::borrow::Cow<'static, str> {
                std::borrow::Cow::Borrowed("Int8")
            }

            fn write_binary<W: std::io::Write>(&self, writer: &mut W) -> Result<(), clicktype_core::error::WriteError> {
                let value: i8 = match self {
                    #(
                        #enum_name::#variant_names => #discriminants,
                    )*
                };
                writer.write_all(&value.to_le_bytes())?;
                Ok(())
            }

            fn read_binary<R: std::io::Read>(reader: &mut R) -> Result<Self, clicktype_core::error::ReadError> {
                let mut buf = [0u8; 1];
                reader.read_exact(&mut buf).map_err(|_| clicktype_core::error::ReadError::UnexpectedEof)?;
                let value = i8::from_le_bytes(buf);

                match value {
                    #(
                        #discriminants => Ok(#enum_name::#variant_names),
                    )*
                    _ => Err(clicktype_core::error::ReadError::InvalidVariant(value)),
                }
            }

            fn fixed_size() -> Option<usize> {
                Some(1)
            }

        }
    };

    TokenStream::from(expanded)
}