mirror of https://github.com/rwf2/Rocket.git
Generate deterministic names for 'uri' macros.
This commit is contained in:
parent
2a1afd12f5
commit
120d1e78da
|
@ -209,9 +209,11 @@ fn internal_uri_macro_decl(route: &Route) -> TokenStream {
|
||||||
// Generate a unique macro name based on the route's metadata.
|
// Generate a unique macro name based on the route's metadata.
|
||||||
let macro_name = route.handler.sig.ident.prepend(crate::URI_MACRO_PREFIX);
|
let macro_name = route.handler.sig.ident.prepend(crate::URI_MACRO_PREFIX);
|
||||||
let inner_macro_name = macro_name.uniqueify_with(|mut hasher| {
|
let inner_macro_name = macro_name.uniqueify_with(|mut hasher| {
|
||||||
route.handler.sig.ident.hash(&mut hasher);
|
route.attr.method.0.hash(&mut hasher);
|
||||||
route.attr.uri.path().hash(&mut hasher);
|
route.attr.uri.path().hash(&mut hasher);
|
||||||
route.attr.uri.query().hash(&mut hasher)
|
route.attr.uri.query().hash(&mut hasher);
|
||||||
|
route.attr.data.as_ref().map(|d| d.value.hash(&mut hasher));
|
||||||
|
route.attr.format.as_ref().map(|f| f.0.hash(&mut hasher));
|
||||||
});
|
});
|
||||||
|
|
||||||
let route_uri = route.attr.uri.to_string();
|
let route_uri = route.attr.uri.to_string();
|
||||||
|
|
|
@ -80,18 +80,21 @@ impl IdentExt for syn::Ident {
|
||||||
self.prepend(crate::ROCKET_IDENT_PREFIX)
|
self.prepend(crate::ROCKET_IDENT_PREFIX)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a unqiue version of the ident `self` based on the hash of `self`,
|
||||||
|
/// its span, the current call site, and any additional information provided
|
||||||
|
/// by the closure `f`.
|
||||||
|
///
|
||||||
|
/// Span::source_file() / line / col are not stable, but the byte span and
|
||||||
|
/// some kind of scope identifier do appear in the `Debug` representation
|
||||||
|
/// for `Span`. And they seem to be consistent across compilations: "#57
|
||||||
|
/// bytes(106..117)" at the time of writing. So we use that.
|
||||||
fn uniqueify_with<F: FnMut(&mut dyn Hasher)>(&self, mut f: F) -> syn::Ident {
|
fn uniqueify_with<F: FnMut(&mut dyn Hasher)>(&self, mut f: F) -> syn::Ident {
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
|
||||||
use std::collections::hash_map::DefaultHasher;
|
use std::collections::hash_map::DefaultHasher;
|
||||||
|
|
||||||
// Keep a global counter (+ thread ID later) to generate unique ids.
|
|
||||||
static COUNTER: AtomicUsize = AtomicUsize::new(0);
|
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
self.hash(&mut hasher);
|
self.hash(&mut hasher);
|
||||||
std::process::id().hash(&mut hasher);
|
format!("{:?}", self.span()).hash(&mut hasher);
|
||||||
std::thread::current().id().hash(&mut hasher);
|
format!("{:?}", Span::call_site()).hash(&mut hasher);
|
||||||
COUNTER.fetch_add(1, Ordering::AcqRel).hash(&mut hasher);
|
|
||||||
f(&mut hasher);
|
f(&mut hasher);
|
||||||
|
|
||||||
self.append(&format!("_{}", hasher.finish()))
|
self.append(&format!("_{}", hasher.finish()))
|
||||||
|
|
|
@ -0,0 +1,72 @@
|
||||||
|
#[macro_use] extern crate rocket;
|
||||||
|
|
||||||
|
#[get("/")]
|
||||||
|
fn index() { }
|
||||||
|
|
||||||
|
mod module {
|
||||||
|
// This one has all the same macro inputs, and we need it to
|
||||||
|
// generate a crate-wide unique identifier for the macro it
|
||||||
|
// defines.
|
||||||
|
#[get("/")]
|
||||||
|
pub fn index() { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Makes sure that the hashing of the proc macro's call site span
|
||||||
|
// is enough, even if we're inside a declarative macro
|
||||||
|
macro_rules! gen_routes {
|
||||||
|
() => {
|
||||||
|
#[get("/")]
|
||||||
|
pub fn index() { }
|
||||||
|
|
||||||
|
pub mod two {
|
||||||
|
#[get("/")]
|
||||||
|
pub fn index() { }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod module2 {
|
||||||
|
gen_routes!();
|
||||||
|
|
||||||
|
pub mod module3 {
|
||||||
|
gen_routes!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uri_reachability() {
|
||||||
|
use rocket::http::Status;
|
||||||
|
use rocket::local::blocking::Client;
|
||||||
|
|
||||||
|
let rocket = rocket::build()
|
||||||
|
.mount("/", routes![index])
|
||||||
|
.mount("/module", routes![module::index])
|
||||||
|
.mount("/module2", routes![module2::index])
|
||||||
|
.mount("/module2/two", routes![module2::two::index])
|
||||||
|
.mount("/module2/module3", routes![module2::module3::index])
|
||||||
|
.mount("/module2/module3/two", routes![module2::module3::two::index]);
|
||||||
|
|
||||||
|
let uris = rocket.routes()
|
||||||
|
.map(|r| r.uri.base().to_string())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let client = Client::debug(rocket).unwrap();
|
||||||
|
for uri in uris {
|
||||||
|
let response = client.get(uri).dispatch();
|
||||||
|
assert_eq!(response.status(), Status::Ok);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_uri_calls() {
|
||||||
|
let uris = [
|
||||||
|
uri!(index()),
|
||||||
|
uri!(module::index()),
|
||||||
|
uri!(module2::index()),
|
||||||
|
uri!(module2::two::index()),
|
||||||
|
uri!(module2::module3::index()),
|
||||||
|
uri!(module2::module3::two::index()),
|
||||||
|
];
|
||||||
|
|
||||||
|
assert!(uris.iter().all(|uri| uri == "/"));
|
||||||
|
}
|
Loading…
Reference in New Issue