cute_custom_default 2.2.0

Derive macro for `Default` trait with customization
Documentation
use quote::quote;

use std::fmt;

pub struct CustomDefault {
    ast: syn::DeriveInput,
    fields: Vec<(String, String)>,
}

impl CustomDefault {
    pub fn new(ast: syn::DeriveInput) -> Self {
        let struct_data = match ast.data {
            syn::Data::Struct(ref data) => data,
            _ => panic!("derive(CustomDefault) is available for structs only"),
        };

        let mut fields = Vec::new();

        for field in struct_data.fields.iter() {
            let variable_name = field.ident.as_ref().expect("Named field is needed").to_string();
            let mut initalizer = "Default::default()".to_owned();

            for attr in field.attrs.iter() {
                let meta = &attr.meta;

                if meta.path().is_ident("def_exp") {
                    match meta {
                        syn::Meta::NameValue(ref value) => {
                            let literal = match &value.value {
                                syn::Expr::Lit(literal) => &literal.lit,
                                _ => panic!("'def_exp' expected literal value"),
                            };
                            match literal {
                                syn::Lit::Str(ref value) => {
                                    initalizer = value.value();
                                    break;
                                }
                                _ => panic!("'def_exp' should be string, correct format is #[def_exp=\"expression\"]"),
                            }
                        },
                        _ => panic!("'def_exp' has incorrect format, expects #[def_exp=\"expression\"]"),
                    }
                } else if meta.path().is_ident("def_val") {
                    match meta {
                        syn::Meta::NameValue(ref value) => {
                            let literal = match &value.value {
                                syn::Expr::Lit(literal) => &literal.lit,
                                _ => panic!("'dev_val' expected literal value"),
                            };

                            match literal {
                                syn::Lit::Str(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => {
                                        if path.path.is_ident("String") {
                                            initalizer = format!("\"{}\".to_owned()", value.value());
                                        } else {
                                            initalizer = format!("\"{}\"", value.value());
                                        }
                                        break;
                                    },
                                    syn::Type::Reference(ref reference) => {
                                        match &*reference.elem {
                                            syn::Type::Path(ref path) if path.path.is_ident("str") => (),
                                            _ => panic!("'def_val' with string is placed on field '{}', but it is not string", variable_name),
                                        }

                                        initalizer = format!("\"{}\"", value.value());
                                        break;
                                    },
                                    _ => panic!("'def_val' with string is placed on field '{}', but it is not string", variable_name),
                                },
                                syn::Lit::ByteStr(ref value) => match field.ty {
                                    syn::Type::Array(_) => {
                                        initalizer.clear();

                                        initalizer.push('[');
                                        for byte in value.value() {
                                            initalizer.push_str(&format!("{},", byte));
                                        }
                                        initalizer.pop();
                                        initalizer.push(']');

                                        break;
                                    },
                                    syn::Type::Reference(ref reference) => {
                                        match &*reference.elem {
                                            syn::Type::Slice(_) => (),
                                            _ => panic!("'def_val' expects bytes slice, but field '{}' got some other reference", variable_name),
                                        }

                                        initalizer.clear();

                                        initalizer.push('&');
                                        initalizer.push('[');
                                        for byte in value.value() {
                                            initalizer.push_str(&format!("{},", byte));
                                        }
                                        initalizer.pop();
                                        initalizer.push(']');

                                        break;
                                    },
                                    _ => panic!("'def_val' expects bytes type for field '{}'", variable_name),
                                },
                                syn::Lit::Byte(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => {
                                        if path.path.is_ident("u8") {
                                            initalizer = format!("{}", value.value());
                                        } else if path.path.is_ident("i8") {
                                            initalizer = format!("{} as i8", value.value());
                                        } else {
                                            panic!("'def_val' is byte literal, but field '{}' is not u8/i8", variable_name);
                                        }
                                        break;
                                    },
                                    _ => panic!("'def_val' is byte literal, but field '{}' is not u8/i8", variable_name),
                                },
                                syn::Lit::Char(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => if path.path.is_ident("char") {
                                        initalizer = format!("'{}'", value.value());
                                        break;
                                    },
                                    _ => panic!("'def_val' is char literal, but field '{}' is not char", variable_name),
                                },
                                syn::Lit::Int(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => {
                                        if path.path.is_ident("u8") {
                                            initalizer = format!("{}", value.base10_parse::<u8>().unwrap());
                                        } else if path.path.is_ident("i8") {
                                            initalizer = format!("{}", value.base10_parse::<i8>().unwrap());
                                        } else if path.path.is_ident("u16") {
                                            initalizer = format!("{}", value.base10_parse::<u16>().unwrap());
                                        } else if path.path.is_ident("i16") {
                                            initalizer = format!("{}", value.base10_parse::<i16>().unwrap());
                                        } else if path.path.is_ident("u32") {
                                            initalizer = format!("{}", value.base10_parse::<u32>().unwrap());
                                        } else if path.path.is_ident("i32") {
                                            initalizer = format!("{}", value.base10_parse::<i32>().unwrap());
                                        } else if path.path.is_ident("u64") {
                                            initalizer = format!("{}", value.base10_parse::<u64>().unwrap());
                                        } else if path.path.is_ident("i64") {
                                            initalizer = format!("{}", value.base10_parse::<i64>().unwrap());
                                        } else if path.path.is_ident("u128") {
                                            initalizer = format!("{}", value.base10_parse::<u128>().unwrap());
                                        } else if path.path.is_ident("i128") {
                                            initalizer = format!("{}", value.base10_parse::<i128>().unwrap());
                                        } else {
                                            panic!("'def_val' is integer literal, but field '{}' is not integer", variable_name);
                                        }
                                        break;
                                    },
                                    _ => panic!("'def_val' is integer literal, but field '{}' is not integer", variable_name),
                                },
                                syn::Lit::Float(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => {
                                        if path.path.is_ident("f32") {
                                            initalizer = format!("{}", value.base10_parse::<f32>().unwrap());
                                        } else if path.path.is_ident("f64") {
                                            initalizer = format!("{}", value.base10_parse::<f64>().unwrap());
                                        } else {
                                            panic!("'def_val' is float literal, but field '{}' is not float", variable_name)
                                        }
                                        break;
                                    },
                                    _ => panic!("'def_val' is float literal, but field '{}' is not float", variable_name),
                                },
                                syn::Lit::Bool(ref value) => match field.ty {
                                    syn::Type::Path(ref path) => if path.path.is_ident("bool") {
                                        initalizer = format!("{}", value.value);
                                        break;
                                    },
                                    _ => panic!("'def_val' is bool literal, but field '{}' is not bool", variable_name),
                                },
                                _ => panic!("'def_val' expression has unknown format"),
                            } //match literal
                        },
                        _ => panic!("'def_val' should has value, correct format is #[def_val=<expression>]"),
                    }
                }
            }

            fields.push((variable_name, initalizer))
        }

        Self {
            ast,
            fields
        }
    }

    pub fn generate(&self) -> proc_macro::TokenStream {
        let text = self.to_string();

        match text.parse() {
            Ok(res) => res,
            Err(error) => panic!("Error: {:?}\nGenerated code: {}", error, text)
        }
    }
}

impl fmt::Display for CustomDefault {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (impl_gen, type_gen, where_clause) = self.ast.generics.split_for_impl();

        writeln!(f, "{} Default for {}{} {{", quote!(impl#impl_gen), self.ast.ident, quote!(#type_gen #where_clause))?;
        writeln!(f, "    fn default() -> Self {{")?;

        writeln!(f, "        Self {{")?;

        for (key, value) in self.fields.iter() {
            writeln!(f, "            {}: {},", key, value)?;
        }

        writeln!(f, "        }}")?;
        writeln!(f, "\n    }}")?;
        writeln!(f, "}}")
    }
}