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 SchemaStruct(syn::ItemStruct);

impl SchemaStruct {
    pub fn new(input: syn::ItemStruct) -> Self {
        Self(input)
    }
}

impl SchemaStruct {
    pub fn render(self) -> proc_macro::TokenStream {
        let mut strct = self.0;
        let struct_name = &strct.ident;
        let formated_struct_name = struct_name.to_string().to_case(Case::Snake);

        let mut struct_fn_schema_stream: proc_macro2::TokenStream = proc_macro2::TokenStream::new();
        let mut struct_fn_schema_value_stream: proc_macro2::TokenStream =
            proc_macro2::TokenStream::new();

        let mut struct_fn_schema = vec![];
        let mut struct_fn_schema_value = vec![];

        struct_fn_schema.push(format!("let mut {} = VecMap::new();", formated_struct_name));

        struct_fn_schema_value.push(format!("let mut {} = VecMap::new();", formated_struct_name));

        if let Fields::Named(fields) = &mut strct.fields {
            fields.named.iter_mut().for_each(|f| {
                let field_ident = f.ident.as_ref().unwrap().to_string();

                if field_ident.starts_with("placeholder_") {
                    return;
                }

                let type_ident = type_to_ident(&field_ident, &f.ty).unwrap();
                struct_fn_schema.push(format!(
                    "{}.insert(String::from(\"{}\"), {}::schema());",
                    formated_struct_name, field_ident, type_ident
                ));

                struct_fn_schema_value.push(format!(
                    "{}.insert(
                        String::from(\"{}\"), self.{}.schema_value()
                    );",
                    formated_struct_name, field_ident, field_ident
                ));
            });
        } else if let Fields::Unnamed(fields) = &mut strct.fields {
            fields.unnamed.iter_mut().enumerate().for_each(|(i, f)| {
                let type_ident = type_to_ident_unnamed(i, &f.ty).unwrap();
                struct_fn_schema.push(format!(
                    "{}.insert(String::from(\"{}\"), {}::schema());",
                    formated_struct_name, i, type_ident
                ));

                struct_fn_schema_value.push(format!(
                    "{}.insert(
                        String::from(\"{}\"), self.{}.schema_value()
                    );",
                    formated_struct_name, i, i
                ));
            });
        }

        struct_fn_schema.push(format!("Schema::Object({})", formated_struct_name));
        struct_fn_schema_value.push(format!("SchemaValue::Object({})", formated_struct_name));

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

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

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

                fn schema_value(&self) -> SchemaValue {
                    #struct_fn_schema_value_stream
                }
            }
        };

        stream.into()
    }
}