hpl-toolkit-derive-to-schema 0.0.6

HPL attribute derive for defining a schema
Documentation
use crate::utils::{type_to_ident, type_to_ident_unnamed};
use convert_case::{Case, Casing};
use quote::quote;
use std::str::FromStr;
use syn::Fields;

pub struct SchemaEnum(syn::ItemEnum);

impl SchemaEnum {
    pub fn new(input: syn::ItemEnum) -> Self {
        Self(input)
    }
}

impl SchemaEnum {
    pub fn render(self) -> proc_macro::TokenStream {
        // User Defined Schema
        let mut enm = self.0.clone();
        let enum_name = &enm.ident;

        let mut schema_fn_schema_stream: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
        let mut schema_fn_schema_value_stream: proc_macro2::TokenStream =
            proc_macro2::TokenStream::new();

        let mut schema_fn_schema_variants = vec![format!("let mut variants = VecMap::<String, Schema>::new();")]; 

        // Iterating through User Defined Schema Variants
        enm.variants.iter_mut().for_each(|variant| {
            // User Defined Schema Variat
            let variant_name = &variant.ident;
            let formated_variant_name = variant_name
                .to_string()
                .to_case(Case::Snake)
                .parse::<proc_macro2::TokenStream>()
                .unwrap();

            let mut schema_fn_schema_value_variant = vec![];


            if matches!(variant.fields, Fields::Unit) {
                schema_fn_schema_variants.push(format!(
                    "variants.insert(String::from(\"{}\"), Schema::Null);",
                    variant_name
                ));

                schema_fn_schema_value_variant.push(format!(
                    "Self::{} => SchemaValue::Enum(String::from(\"{}\"), Box::new(SchemaValue::Null)),",
                    variant_name, variant_name
                ));
            } else {

                schema_fn_schema_variants.push(format!(
                    "let mut {} = VecMap::new();",
                    formated_variant_name
                ));
    
                
                schema_fn_schema_value_variant.push(format!("Self::{}  ", variant_name));
                schema_fn_schema_value_variant.push(format!(
                    "let mut {} = VecMap::new();",
                    formated_variant_name
                ));

                if let Fields::Named(fields) = &mut variant.fields {
                    schema_fn_schema_value_variant[0].push_str("{");
                    fields.named.iter_mut().for_each(|field| {
                        // User Defined Schema Variant Fields
                        let ident = field.ident.as_ref().unwrap().to_string();
    
                        // Generated Implementation for fn schema() for User Defined Schema
                        let type_ident = type_to_ident(&ident, &field.ty).unwrap();
                        schema_fn_schema_variants.push(format!(
                            "{}.insert(String::from(\"{}\"), {}::schema());",
                            formated_variant_name, ident, type_ident
                        ));
    
                        // Generated Implementation for fn schema_value() for User Defined Schema
                        schema_fn_schema_value_variant[0].push_str(&format!("{}, ", ident));
                        schema_fn_schema_value_variant.push(format!(
                            "{}.insert( String::from(\"{}\"), {}.schema_value() );",
                            formated_variant_name, ident, ident
                        ));
                    });
    
                    schema_fn_schema_value_variant[0].push_str("} => {");
                } else if let Fields::Unnamed(fields) = &mut variant.fields {
                    schema_fn_schema_value_variant[0].push_str("(");
    
                    fields.unnamed.iter_mut().enumerate().for_each(|(i, f)| {
                        let type_ident = type_to_ident_unnamed(i, &f.ty).unwrap();
    
                        schema_fn_schema_variants.push(format!(
                            "{}.insert(String::from(\"{}\"), {}::schema());",
                            formated_variant_name, i, type_ident
                        ));
    
                        schema_fn_schema_value_variant[0].push_str(&format!("item_{}, ", i));
                        schema_fn_schema_value_variant.push(format!(
                            "{}.insert( String::from(\"{}\"), item_{}.schema_value() );",
                            formated_variant_name, i, i
                        ));
                    });
    
                    schema_fn_schema_value_variant[0].push_str(") => {");
                }

                
                schema_fn_schema_variants.push(format!(
                    "variants.insert(String::from(\"{}\"), Schema::Object({}));",
                    variant_name, formated_variant_name
                ));

                schema_fn_schema_value_variant.push(format!(
                    "SchemaValue::Enum(String::from(\"{}\"), Box::new(SchemaValue::Object({})))",
                    variant_name, formated_variant_name
                ));
                schema_fn_schema_value_variant.push("}".to_string());
            }

            schema_fn_schema_value_stream.extend::<proc_macro2::TokenStream>(
                proc_macro2::TokenStream::from_str(
                    &schema_fn_schema_value_variant
                        .into_iter()
                        .fold(String::new(), |acc, s| acc + &s),
                )
                .unwrap(),
            );
        });

        schema_fn_schema_variants.push("Schema::Enum(variants)".to_string());
        schema_fn_schema_stream.extend::<proc_macro2::TokenStream>(
            proc_macro2::TokenStream::from_str(
                &schema_fn_schema_variants
                    .into_iter()
                    .fold(String::new(), |acc, s| acc + &s),
            )
            .unwrap(),
        );

        let stream: proc_macro2::TokenStream = quote! {
            impl ToSchema for #enum_name {
                fn schema() -> Schema {
                    #schema_fn_schema_stream
                }

                fn schema_value(&self) -> SchemaValue {
                    match self {
                        #schema_fn_schema_value_stream
                        _ => {
                            panic!("Unsupported version");
                        }
                    }
                }
            }
        };

        stream.into()
    }
}