shori 0.1.0

Flexible parsing and transformation utilities for structured data. #[derive(Parser)] adds methods like .parse().json(), .toml(), .bin(), .map(), and .from() to simplify conversions using common wrappers like Arc, Mutex, Box, OnceCell, etc.
Documentation
use mokuya::components::prelude::{get_fields, get_struct_name};
use quote::{format_ident, quote};
use syn::DeriveInput;

pub fn generate_parse_by_field(input: &DeriveInput) -> proc_macro2::TokenStream {
    let struct_ident = get_struct_name(input);
    let struct_name_str = struct_ident.to_string();
    let fields = get_fields(input).expect("fields must be provided");

    let mut inner_wrappers = Vec::new();
    let mut outer_wrappers = Vec::new();
    let mut selector_methods = Vec::new();
    let mut per_field_feature_wrappers = Vec::new();
    let mut per_field_feature_methods = Vec::new();

    for field in fields.iter() {
        let field_ident = field.ident.as_ref().expect("named fields only");
        let field_ty = &field.ty;

        let field_name_str = field_ident.to_string();
        let mut chars = field_name_str.chars();
        let capitalized = match chars.next() {
            Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
            None => field_name_str.clone(),
        };

        let inner_ident = format_ident!("Parse{}{}", struct_name_str, capitalized);
        let outer_ident = format_ident!("Field{}", capitalized);
        let outer_ident_str = outer_ident.to_string();

        inner_wrappers.push(quote! {
            pub struct #inner_ident(pub #field_ty);
        });

        outer_wrappers.push(quote! {
            pub struct #outer_ident(pub #inner_ident);
        });

        selector_methods.push(quote! {
            pub fn #field_ident(self) -> #outer_ident {
                #outer_ident(#inner_ident(self.0.0.#field_ident))
            }
        });

        let arc_ident = format_ident!("{}Arc", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "arc")]
            pub struct #arc_ident(pub std::sync::Arc<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "arc")]
            impl #outer_ident {
                pub fn arc(self) -> #arc_ident {
                    #arc_ident(std::sync::Arc::new(self.0.0))
                }
            }
        });

        let tokio_mutex_ident = format_ident!("{}TokioMutex", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "tokio")]
            pub struct #tokio_mutex_ident(pub tokio::sync::Mutex<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "tokio")]
            impl #outer_ident {
                pub fn tokio_mutex(self) -> #tokio_mutex_ident {
                    #tokio_mutex_ident(tokio::sync::Mutex::new(self.0.0))
                }
            }
        });

        let mutex_ident = format_ident!("{}Mutex", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "mutex")]
            pub struct #mutex_ident(pub std::sync::Mutex<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "mutex")]
            impl #outer_ident {
                pub fn mutex(self) -> #mutex_ident {
                    #mutex_ident(std::sync::Mutex::new(self.0.0))
                }
            }
        });

        let box_ident = format_ident!("{}Box", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "box")]
            pub struct #box_ident(pub Box<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "box")]
            impl #outer_ident {
                pub fn boxed(self) -> #box_ident {
                    #box_ident(Box::new(self.0.0))
                }
            }
        });

        let refcell_ident = format_ident!("{}RefCell", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "refcell")]
            pub struct #refcell_ident(pub std::cell::RefCell<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "refcell")]
            impl #outer_ident {
                pub fn ref_cell(self) -> #refcell_ident {
                    #refcell_ident(std::cell::RefCell::new(self.0.0))
                }
            }
        });

        let unsafecell_ident = format_ident!("{}UnsafeCell", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "unsafecell")]
            pub struct #unsafecell_ident(pub std::cell::UnsafeCell<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "unsafecell")]
            impl #outer_ident {
                pub fn unsafe_cell(self) -> #unsafecell_ident {
                    #unsafecell_ident(std::cell::UnsafeCell::new(self.0.0))
                }
            }
        });

        let oncecell_ident = format_ident!("{}OnceCell", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "oncecell")]
            pub struct #oncecell_ident(pub std::cell::OnceCell<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "oncecell")]
            impl #outer_ident {
                pub fn once_cell(self) -> #oncecell_ident {
                    let cell = std::cell::OnceCell::new();
                    let _ = cell.set(self.0.0);
                    #oncecell_ident(cell)
                }
            }
        });

        let vec_ident = format_ident!("{}Vec", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "vec")]
            pub struct #vec_ident(pub Vec<#field_ty>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "vec")]
            impl #outer_ident {
                pub fn vec(self) -> #vec_ident {
                    #vec_ident(vec![self.0.0])
                }
            }
        });

        let bin_ident = format_ident!("{}Bin", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "bincode")]
            pub struct #bin_ident(pub Vec<u8>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "bincode")]
            impl #outer_ident {
                pub fn bin(self) -> Result<#bin_ident, Box<dyn std::error::Error>> {
                    let config = bincode::config::standard();
                    let serialized = bincode::encode_to_vec(&self.0.0, config)?;
                    Ok(#bin_ident(serialized))
                }
            }
        });

        let json_ident = format_ident!("{}Json", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "serde_json")]
            pub struct #json_ident(pub serde_json::Value);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "serde_json")]
            impl #outer_ident {
                pub fn json(self) -> Result<#json_ident, Box<dyn std::error::Error>> {
                    let json = serde_json::to_value(self.0.0)?;
                    Ok(#json_ident(json))
                }
            }
        });

        let hashmap_ident = format_ident!("{}HashMap", outer_ident_str);
        per_field_feature_wrappers.push(quote! {
            #[cfg(feature = "hashmap")]
            pub struct #hashmap_ident(pub std::collections::HashMap<String, Box<dyn std::any::Any + Send + Sync>>);
        });
        per_field_feature_methods.push(quote! {
            #[cfg(feature = "hashmap")]
            impl #outer_ident {
                pub fn hashmap(self) -> #hashmap_ident {
                    let mut map = std::collections::HashMap::new();
                    map.insert(
                        stringify!(#field_ident).to_string(),
                        Box::new(self.0.0) as Box<dyn std::any::Any + Send + Sync>
                    );
                    #hashmap_ident(map)
                }
            }
        });
    }

    quote! {
        pub struct FieldSelector(pub Parse);
        #(#inner_wrappers)*
        #(#outer_wrappers)*
        #(#per_field_feature_wrappers)*
        impl FieldSelector {
            #(#selector_methods)*
        }
        #(#per_field_feature_methods)*
    }
}