Converting Rust structs to HashMap format using custom field annotations for API integration

I need help with my Rust project where I’m connecting to an external API that requires data in HashMap format with specific property types.

I want to avoid writing the same conversion code over and over again. Instead, I’d like to use custom attributes on my struct fields to automatically generate the right format.

Here’s what I’m trying to achieve with a struct like this:

#[derive(Serialize, Deserialize)]
struct Employee {
    #[api_field(property = "title", label = "Full Name")]
    pub full_name: String,

    #[api_field(property = "url", label = "Profile Picture")]
    pub avatar_url: Option<String>,

    #[api_field(property = "MultiSelect", label = "Skills")]
    pub skill_list: Option<Vec<serde_json::Value>>,
}

I want this to automatically convert into something like:

let mut result = HashMap::new();
result.insert("Full Name".to_string(), ApiProperty::Title(
    vec![
        TextContent {
            formatting: None,
            content_type: Some(TextType::Plain),
            formula: None,
            reference: None,
            display_text: None,
            link: None,
            text_data: TextData { 
                value: employee.full_name.clone(), 
                url: None 
            }
        }
    ]
));

if let Some(avatar) = employee.avatar_url.clone() {
    result.insert("Profile Picture".to_string(), ApiProperty::Url(avatar));
}

result.insert("Skills".to_string(), ApiProperty::MultiSelect(
    employee.skill_list.iter().map(|item| SelectItem {
        identifier: None,
        theme: None,
        title: format!("{}", item)
    }).collect()
));

How can I implement this kind of automatic conversion using procedural macros or derive macros in Rust?

check out the derive_builder crate - it’s got good examples of handling custom attributes. you’ll need to parse field annotations in your proc macro and generate match arms for each property type. I’d start with just one field type tho, this looks pretty complex for a first macro.

Been working with proc macros for API conversions lately - ran into the same stuff. Treat it as two phases: extract your attribute metadata first, then generate the type-specific conversion logic. I’d create an intermediate representation that captures field mappings before generating HashMap code. Store the label, property type, and field name in a struct during parsing, then iterate through these to build conversion methods. That TextContent wrapping for titles is annoying as hell. I ended up creating a trait with default implementations for each API property type. Your macro just calls the trait methods instead of generating all that boilerplate inline. Handling Option types cleanly needs some pattern matching in your generated code, but it’s manageable once you nail the basic structure.

I’ve built something similar for a different API integration. Use syn and quote crates to parse your custom attributes and generate the conversion code. Create a derive macro that grabs the api_field attribute parameters and matches the property type to generate the right ApiProperty variant. The tricky bit is nested structures like TextContent - I made helper functions for each property type instead of cramming everything into the macro. Consider using darling crate over raw syn parsing since it makes attribute handling way cleaner. Start simple and add complexity as you go.