Currently, given this code:
#[get("/documents/<id>")]
fn get_document(id: usize) ...
The get_document route can be matched against for incoming requests to e.g. the URI /documents/0, but there's no way to, given the get_document route and id: usize = 0, get back a string or URI which would match the route when requested. This is present in many other web frameworks, sometimes called url_for. The general utility is that you have a single source of truth for routing, instead of one source of truth for matching routes and separate ad-hoc URI generation, which isn't guaranteed to line up or stay synchronized. Adding this to rocket means that the URI generation can be generated as part of rocket_codegen and type-safe on inputs.
This could be exposed as something like url_for!(get_document, id = 0), though routes can be mounted at places other than / on the Rocket.
I think this feature can be useful for the Created type that needs a route as a String.
Your function (macro*) can be used to check at compile time the validity of the route and don't point anywhere (404).
This could indeed provide compile-time checks similar to how Yesod does this! :-)
This is now implemented in master! The best documentation on how this works can be found in:
In short, for a route like:
#[get("/person/<name>/<age>")]
fn person(name: String, age: u8) -> String {
format!("Hello {}! You're {} years old.", name, age)
}
A URI can be created as follows:
```rust,ignore
// with unnamed parameters
let mike = uri!(person: "Mike", 28);
// with named parameters
let mike = uri!(person: name = "Mike", age = 28);
let mike = uri!(person: age = 28, name = "Mike");
// with a specific mount-point
let mike = uri!("/api", person: name = "Mike", age = 28);
A lot of effort has gone into making `uri!` as intuitive and useable as possible. Nonetheless, I would appreciate any testing and feedback. If you're able, you can use type-safe URI generation now by depending on Rocket straight from GitHub:
```toml
[dependencies]
rocket = { git = "https://github.com/SergioBenitez/Rocket" }
rocket_codegen = { git = "https://github.com/SergioBenitez/Rocket" }
Note that master contains breaking changes. In particular, URI is now Uri, and the decl_macro feature is required. Please take it for a spin if you can! Any feedback is greatly appreciated.
That's really cool! I like how it's just a thin wrapper around format!.
Would it be possible for it to return a std::fmt::Arguments instead of a String? Maud is moving toward zero allocations (lfairy/maud#90) so it would be cool to support that.
@lfairy It actually returns a Uri, but it does allocate once to hold the internal String. A value of Arguments doesn't contain the necessary information to reconstruct the URI, but we can imagine an analogous type that does. I'm not sure the potential usability implications are worth the hassle, however.
On a related note, a nice optimization would be to format, at compile time, invocations with only literals, saving an allocation in that case.
Most helpful comment
This is now implemented in master! The best documentation on how this works can be found in:
uri!UriDisplayFromUriParamIn short, for a route like:
A URI can be created as follows:
```rust,ignore
// with unnamed parameters
let mike = uri!(person: "Mike", 28);
// with named parameters
let mike = uri!(person: name = "Mike", age = 28);
let mike = uri!(person: age = 28, name = "Mike");
// with a specific mount-point
let mike = uri!("/api", person: name = "Mike", age = 28);
Note that master contains breaking changes. In particular,
URIis nowUri, and thedecl_macrofeature is required. Please take it for a spin if you can! Any feedback is greatly appreciated.