Skip to content

Idea of decorator: proc macro for fn. #1923

Closed
@hh9527

Description

@hh9527

Some thing like @decorator in python or #[derive()] for fn

struct User { /* ... */ }
impl FromRequest for User {
    // ...
}

#[decorate(HttpHandler)]
#[http(get("/"))]
fn index(user: User) -> String {
    format!("hello, {}", user.name)
}

#[decorate(HttpHandler)]
#[http(get("/users/<id>"))]
fn get(user: User, id: String) -> Result<String, Status> {
    if user.id != id {
      Err(Status::Forbidden)
    } else {
      Ok(format!("name: {}, last login at: {}", user.name, user.last_login_time))
    }
}

fn main() {
    HttpServer::new()
       .mount("/", vec![index, get])
       .launch()
       .expect("Failed to start server");
}

For now, this can be done by syntax plugin, but it will far from landing in stable (perhaps never).
See: https://github.com/SergioBenitez/Rocket (the example above is almost same as Rocket's example)

So maybe it can be done via same mechanism as derive in proc macro:

The codegen crate will receive the signature of fn and output a new decorated fn and a inner function name , the inner function name will replace the old one. for example:

#[decorate(HttpHandler)]
#[http(get("/users/<id>"))]
fn get(user: User, id: String) -> Result<String, Status> {
    // ...
}

codegen crate will receive

#[http(get("/users/<id>"))]
fn get(user: User, id: String) -> Result<String, Status> {}

and parse (via syn as current proc macro codegen crates) and produce (via quote) :

fn get(request: Request) -> Result<Response, Status> {
    let user = User::from_request(&request)?;

    let params = request.parse_parms("/user/<id>")?;
    let id = params.get(1)?;

    let body = __http_inner_get(user, id)?;
    Ok(Response::build()
        .header("Content-Type", "text/plain")
        .sized_body(Cursor::new(body))
        .ok())
}

and a inner function name for original function: '__http_inner_get'.

And then rustc will replace the original function's name with the given one. This is the only difference with deriving a struct/enum of current proc macro mechanism.

It is like python's decorator:

def require_user(request):
    return db.load_user_by_session(request.cookie.get("session"))

@http.get("/users/<id:str>", from_request = [require_user])
def get(user, id):
  # do something
  return "hello %s" % user.name

The semantic of decorator is: function = decorator(old_function)
If multi decorator is used, then pipe them one by one:
function = decorator1(decorator2(old_function))

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions