Proc macro: Applying all default parameters to a struct

Hi, I have a problem dealing with default parameters in structs using procedural macros.

I am working on a procedural macro that automatically generated capnp serialization code. From the user point of view, this is what it looks like:

#[capnp_conv(test_capnp::test_struct)]
struct TestStruct {
    my_bool: bool,
    my_int8: i8,
    my_int16: i16,
    my_int32: i32,
    my_int64: i64,
}

This automatically provides serialization and deserialization methods for the user.
I would like this macro to work exactly the same when I have a default parameter for the struct, as follows:

#[capnp_conv(test_capnp::test_struct)]
struct TestStruct <A = i16> {
    my_bool: bool,
    my_int8: i8,
    my_int16: A,
    my_int32: i32,
    my_int64: i64,
}

I am pretty sure that this should be possible, as it seems like serde is capable of doing this. I tried to find the relevant code that does this in the serde repository, but from a first look I couldn’t find it.

The solution I was thinking of was something like a pre-step where I take all the default parameters of the struct and assign them everywhere inside the struct. I was wondering if there is an elegant way of doing this, not having to recursively going through all the tree, matching on every token type.

Note that I also want this example to work:

#[capnp_conv(test_capnp::test_struct)]
struct TestStruct <A = i16> {
    my_bool: bool,
    my_int8: i8,
    my_int16: Vec<A>,
    my_int32: i32,
    my_int64: i64,
}

This means a simple substitution might not be trivial. Any ideas are appreciated!

I managed to implement a reasonable solution for this problem.
For future readers, I include it here:

/// Obtain a map of default values from generics.
/// Example:
///
/// ```text
/// struct MyStruct<A = u32, B = u64> { ... }
/// ```
///
/// We expect to get a map, mapping A -> u32, B -> u64.
///
pub fn extract_defaults(generics: &syn::Generics) -> HashMap<syn::Ident, syn::Path> {
    let mut defaults = HashMap::new();
    for param in &generics.params {
        let type_param = match *param {
            syn::GenericParam::Type(ref type_param) => type_param,
            _ => continue,
        };

        if type_param.eq_token.is_none() {
            continue;
        };

        let default_type = match &type_param.default {
            Some(default_type) => default_type,
            None => continue,
        };

        let default_type_path = match default_type {
            syn::Type::Path(default_type_path) => default_type_path,
            _ => unimplemented!("Only paths default params are supported"),
        };

        if default_type_path.qself.is_some() {
            unimplemented!("qself is not implemented!");
        }

        defaults.insert(type_param.ident.clone(), default_type_path.path.clone());
    }
    defaults
}

/// For every generic along a path, assign a default value if possible
pub fn assign_defaults_path(path: &mut syn::Path, defaults: &HashMap<syn::Ident, syn::Path>) {
    // Deal with the case of a single Ident: `T`
    if path.segments.len() == 1 {
        let last_segment = match path.segments.last_mut().unwrap() {
            syn::punctuated::Pair::End(last_segment) => last_segment,
            _ => unreachable!(),
        };

        if let syn::PathArguments::None = last_segment.arguments {
            if let Some(default_path) = defaults.get(&last_segment.ident) {
                let _ = std::mem::replace(path, default_path.clone());
                return;
            }
        }
    }

    // Deal with the more general case of a Path with various arguments
    // that should be assigned their default value

    for segment in path.segments.iter_mut() {
        let args = match &mut segment.arguments {
            syn::PathArguments::None => continue,
            syn::PathArguments::AngleBracketed(angle_bracketed) => &mut angle_bracketed.args,
            _ => unimplemented!("Only angle bracketed arguments are supported!"),
        };

        for generic_arg in args.iter_mut() {
            let ty = match generic_arg {
                syn::GenericArgument::Type(ty) => ty,
                _ => unimplemented!(),
            };

            let type_path = match ty {
                syn::Type::Path(type_path) => type_path,
                _ => unimplemented!(),
            };

            if type_path.qself.is_some() {
                unimplemented!();
            }

            // Recursively replace default arguments:
            assign_defaults_path(&mut type_path.path, defaults);
        }
    }
}