Macro doesn't work with custom types but works with predefined types

I am building a macro that automatically generates a Rocket route to call a function. Currently I have this:

create_route!{
    "/hello/<inp>/<inp2>",
    pub fn hello(inp: i32, inp2: i32) {
        println!("Hi");
    }
}

#[macro_export]
macro_rules! create_route {
    ($path:expr, $vis:vis fn $name:ident($($arg:ident: $arg_type:ty),*) $code:block) => {
        $vis fn $name($($arg: $arg_type),*) $code

        #[get($path)]
        $vis fn a_route($($arg: i32),*) {
            $name($($arg),*)
        }
    };
}

Which works but only because inside the macro I use a predefined i32 type. I'd expect to just be able to use $arg_type instead, like this:

create_route!{
    "/hello/<inp>/<inp2>",
    pub fn temp(inp: i32, inp2: i32) {
        println!("Hi");
    }
}

#[macro_export]
macro_rules! create_route {
    ($path:expr, $vis:vis fn $name:ident($($arg:ident: $arg_type:ty),*) $code:block) => {
        $vis fn $name($($arg: $arg_type),*) $code

        #[get($path)]
        $vis fn a_route($($arg: $arg_type),*) {
            $name($($arg),*)
        }
    };
}

but for some reason that doesn't work, instead generating errors:

cannot find value `__req` in this scope

and

cannot find value `__data` in this scope

and

cannot find value `__error` in this scope

Why is this and how can I use different types?

Actually I figured out the issue is with using paste! macro. This doesn't work:

generate_functions! {
    "/hello/<name>",
    hello_route,
    pub fn temp(name: String) {
        println!("one: {}", name);
    }
}

#[macro_export]
macro_rules! generate_functions {
    ($path:expr, $route_name:ident, $vis:vis fn $name:ident($($arg:ident: $arg_type:ty)*) $code:block) => {
        $vis fn $name($($arg: $arg_type)*) $code

        #[get($path)]
        $vis fn $route_name($($arg: $arg_type)*) {
            $name($($arg)*)
        }
    };
}

but this does:

generate_functions! {
    "/hello/<name>",
    hello_route,
    pub fn temp(name: String) {
        println!("one: {}", name);
    }
}

#[macro_export]
macro_rules! generate_functions {
    ($path:expr, $route_name:ident, $vis:vis fn $name:ident($($arg:ident: $arg_type:ty)*) $code:block) => {
        $vis fn $name($($arg: $arg_type)*) $code

        #[get($path)]
        $vis fn hello_route($($arg: $arg_type)*) {
            $name($($arg)*)
        }
    };
}

Seems the issue is with custom named route functions.

If I may ask, what is the difference you expect between

create_route! {
    "/hello/<inp>/<inp2>",
    pub fn hello(inp: i32, inp2: i32) {
        println!("Hi");
    }
}

and

#[get("/hello/<inp>/<inp2>")]
pub fn hello(inp: i32, inp2: i32) {
    println!("Hi!");
}

? It doesn't look like your macro is removing any boilerplate. I am confused as to why you need this.

Good question, in its current form it isn't very helpful, but I want to basically wrap any function with this macro and generate both a rocket endpoint, and a function to call the endpoint through a request. Basically given a function

fn temp(input: i32) -> String {...}

I will generate a Rocket route

#[get("/temp/<inp>")]
pub fn temp_route(inp: i32) -> String {
    temp(inp)
}

and a request function

async fn temp_request(input: i32) -> String {
   reqwest::get(format!("/temp?{}", input))
      .send().await.unwrap()
      .text().await.unwrap()
}

This effectively turns any function where the inputs and outputs are Serializable and Deserializable into a REST endpoint. I'm doing this so that my backend and frontend are linked "statically".

I finally went with a proc macro as the solution. Currently the implementation is fairly limited, but it generates GET APIs for any function that only takes in and outputs owned variables that impl Serialize and Deserialize.

The crate is here: https://crates.io/crates/retrofit

PRs are welcome!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.