Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea of decorator: proc macro for fn. #1923

Closed
hh9527 opened this issue Feb 23, 2017 · 4 comments
Closed

Idea of decorator: proc macro for fn. #1923

hh9527 opened this issue Feb 23, 2017 · 4 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@hh9527
Copy link

hh9527 commented Feb 23, 2017

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))

@clarfonthey
Copy link
Contributor

See #1913.

@keeperofdakeys
Copy link

There are actually three types of procedural macros planned. One of them is proc_macro_attribute, which will be similar to a proc_macro_derive, except unlike derive, you get to modify the thing you call it on. This is a replacement for the old syntax extensions, and is available in nightly in a raw form (the TokenStream api is being redesigned so it's not just strings).

You can also stack them:

#[first_macro]
#[second_macro("you get this as input too!")]
fn foo() { ... }

@hh9527
Copy link
Author

hh9527 commented Feb 24, 2017

@keeperofdakeys thank you, it really make sense!

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Dec 6, 2017
@Centril
Copy link
Contributor

Centril commented Apr 26, 2018

Closing as resolved (since we're getting proc macros...).

@Centril Centril closed this as completed Apr 26, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests

4 participants