rustrust-macros

Split module and function name in macro_rule


I'm doing my first steps with macro_rule! and would like to call my macro like this:

my_macro!(some_module::some_fkt)

The macro should create code like this:

fn some_fkt() {
    some_module::some_fkt();
}

That's of course a simplified example, but the point is, that I need only the name for the function declaration, but the full path when calling it. I tried to match with $var:path and to split the name from it, but found neither build in functionality nor was I able to do the split myself via an additional macro or so. So I probably have to solve it via a better matcher. The closest I could get is:

$($module:ident::)+$method:ident

If I match some_module::some_fkt with this I get an error that the pattern is ambiguous. Same if I replace the + with a *, just with the error in a different place. I don't get that error, because the :: needs to be mached and should make the match clear.

Is there an option to solve my problem with this kind of macro? Or do I have to go for a procedural one?


Solution

  • As a macros argument are parsed without looking ahead, this is a bit awkward to write, but it's possible:

    macro_rules! my_macro {
        ($head:ident $(:: $tail:tt)+) => {
            my_macro!( $head :: ; $($tail),* );
        };
    
        ($($module:ident ::)+ ; $method:ident) => {
                $($module ::)+ $method();
            }
        };
    
        ($($module:ident ::)+ ; $head:ident , $($tail:ident),+) => {
            my_macro!( $($module ::)* $head :: ; $($tail),* );
        };
    }
    

    The first clause just accepts the target syntax with separating by :: and then hands off to the third clause which shifts the first parts of the path over to the bit before ; one segment at a time, when only a single ident is left to the right of ; then the second clause applies and puts together the desired output.

    Of course one could omit clauses one and three and just directly use that macro as my_macro!(some_module::;some_fkt), that is use a different separator for the last part, as well.