0
votes

I have a derive style procedural macro, where i would like to calculate the potential maximum length of the serialized version of a struct. For example, given the struct TestStruct below, i would like to call some function with all the fields to calculate the worst case length.

#[derive(MyLengthProcMacro)]
struct TestStruct {
    f1: u32,
    f2: i64,
    f3: SomeOtherStruct
}

For primitives this could look something like:

fn get_string_length(ty: &Type) -> usize {
    let type_string = quote! { #ty }.to_string();
    match type_string.replace(" ", "").as_str() {
        "str" => panic!("String slices must be annotated with a length using #[at_arg()]"),
        "tuple" => panic!("Tuples are not supported!"),
        "char" => "a".len(),
        "bool" => "false".len(),
        "isize" => format!("{:?}", std::isize::MAX).len(),
        "usize" => format!("{:?}", std::usize::MAX).len(),
        "u8" => format!("{:?}", std::u8::MAX).len(),
        "u16" => format!("{:?}", std::u16::MAX).len(),
        "u32" => format!("{:?}", std::u32::MAX).len(),
        "u64" => format!("{:?}", std::u64::MAX).len(),
        "u128" => format!("{:?}", std::u128::MAX).len(),
        "i8" => format!("{:?}", std::i8::MIN).len(),
        "i16" => format!("{:?}", std::i16::MIN).len(),
        "i32" => format!("{:?}", std::i32::MIN).len(),
        "i64" => format!("{:?}", std::i64::MIN).len(),
        "i128" => format!("{:?}", std::i128::MIN).len(),
        "f32" => format!("{:?}", std::f32::MIN).len(),
        "f64" => format!("{:?}", std::f64::MIN).len(),
        _ => {
            // println!("Unexpected type: {:?}", type_string);
            0
        }
    }
}

The problem i am asking here is regarding any non-primitive fields, eg. f3: SomeOtherStruct. Is there a way to access the fields of fields, in a proc-macro?

1

1 Answers

2
votes

No.

Macros are expanded before that kind of analysis is done, so the compiler has no idea what SomeOtherStruct is at that point.


However, this is not how you should implement that macro! Your way doesn't allow the user to use type aliases.

What you should do is simply use your trait recursively and use sum the <T as MyLength>::my_length() where T is the type of the field for each field.

They manually implement your trait for the primitive types.