How to create custom function attribute

I want to create a macro which creates a custom function with a custom attribute:

create_function!(foo, bar)

The macro looks like this, but doesn't work:

#[macro_export]
macro_rules! create_function {
    ($namespace:ident, $key:ident=> {
        paste! {
            #[get(concat!("/", stringify!($namespace), "/", stringify!($key), "_names"))]
            async fn [<$key _names>](
                app: web::Data<AppState>,
                args: web::Json<Args>,
            ) -> Result<impl Responder, UserError> {
                // Function body
            }
        }
    };
}

The line with "concat!" produces the error: expected string literal.
How do I create a custom string literal for the function attribute?

concat creates a static string slice which is not the same as a string literal. I don't think there's a way of creating a string literal by concatenation inside a declarative macro. I think you'd have to switch to a function-like procedural macro, construct the string inside the macro and embed it in the token stream you return to make it work.

You can use $path:literal to pass a string literal as a second parameter.

If a literal is the only acceptable token in a given place, then you need a literal. They cannot be made from nothing in a declarative macro. proc-macros can do this because they have direct access to outputting anything - even invalid tokens - to the token tree.

#[macro_export]
macro_rules! create_function {
    ($key:ident, $path:literal) => {
        paste! {
            #[get($path)]
            async fn [<$key _names>](
                app: web::Data<AppState>,
                args: web::Json<Args>,
            ) -> Result<impl Responder, UserError> {
                todo!()
            }
        }
    };
}

create_function!(bar, "/foo/bar_names");

You should also specify full paths to all types that the macro needs (it will be very verbose) so that callers don't have to import all of them separately to use the macro.

2 Likes

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.