Allow named field generics in 'UriDisplay' derive.

This commit is contained in:
Sergio Benitez 2021-07-01 06:02:40 -07:00
parent 416f42bca7
commit 9d20c57d91
3 changed files with 52 additions and 10 deletions

View File

@ -26,6 +26,7 @@ fn generic_bounds_mapper(bound: StaticTokens) -> MapperBuild {
let bounds = fields.iter()
.filter(|f| !f.ty.is_concrete(&generic_idents))
.map(|f| &f.field.inner.ty)
.map(move |ty| quote_spanned!(ty.span() => #ty: #bound));
Ok(quote!(#(#bounds,)*))

View File

@ -1,6 +1,8 @@
use rocket::local::asynchronous::Client;
use rocket::http::{Status, ContentType, Cookie};
use rocket::response::Responder;
use rocket::serde::json::Json;
use rocket::http::Accept;
#[derive(Responder)]
pub enum Foo<'r> {
@ -108,15 +110,13 @@ async fn responder_baz() {
assert_eq!(r.body_mut().to_string().await.unwrap(), "just a custom");
}
use rocket::serde::json::Json;
// The bounds `Json<T>: Responder, E: Responder` will be added to the generated
// implementation. This would fail to compile otherwise.
#[derive(Responder)]
enum MyResult<'a, T, E, H> {
enum MyResult<'a, T, E, H1, H2> {
Ok(Json<T>),
#[response(status = 404)]
Err(E, H),
Err(E, H1, H2),
#[response(status = 500)]
Other(&'a str),
}
@ -127,19 +127,19 @@ async fn generic_responder() {
let local_req = client.get("/");
let req = local_req.inner();
let v: MyResult<_, (), ContentType> = MyResult::Ok(Json("hi"));
let v: MyResult<_, (), ContentType, Cookie<'static>> = MyResult::Ok(Json("hi"));
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::Ok);
assert_eq!(r.content_type().unwrap(), ContentType::JSON);
assert_eq!(r.body_mut().to_string().await.unwrap(), "\"hi\"");
let v: MyResult<(), &[u8], _> = MyResult::Err(&[7, 13, 23], ContentType::JPEG);
let v: MyResult<(), &[u8], _, _> = MyResult::Err(&[7, 13, 23], ContentType::JPEG, Accept::Text);
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::NotFound);
assert_eq!(r.content_type().unwrap(), ContentType::JPEG);
assert_eq!(r.body_mut().to_bytes().await.unwrap(), vec![7, 13, 23]);
let v: MyResult<(), &[u8], ContentType> = MyResult::Other("beep beep");
let v: MyResult<(), &[u8], ContentType, Accept> = MyResult::Other("beep beep");
let mut r = v.respond_to(req).unwrap();
assert_eq!(r.status(), Status::InternalServerError);
assert_eq!(r.content_type().unwrap(), ContentType::Text);

View File

@ -10,6 +10,19 @@ macro_rules! assert_uri_display_query {
)
}
macro_rules! assert_query_form_roundtrip {
($T:ty, $v:expr) => ({
use rocket::form::{Form, Strict};
use rocket::http::RawStr;
let v = $v;
let string = format!("{}", &v as &dyn UriDisplay<Query>);
let raw = RawStr::new(&string);
let value = Form::<Strict<$T>>::parse_encoded(raw).map(|s| s.into_inner());
assert_eq!(value.expect("form parse"), v);
})
}
macro_rules! assert_query_value_roundtrip {
($T:ty, $v:expr) => ({
use rocket::form::{Form, Strict};
@ -213,12 +226,12 @@ fn uri_display_path() {
fn uri_display_serde() {
use rocket::serde::json::Json;
#[derive(Debug, PartialEq, Clone, UriDisplayQuery, Deserialize, Serialize)]
#[derive(Debug, PartialEq, Clone, FromForm, UriDisplayQuery, Deserialize, Serialize)]
#[serde(crate = "rocket::serde")]
struct Bam {
foo: String,
bar: Option<usize>,
baz: Result<String, usize>,
baz: bool,
}
#[derive(Debug, PartialEq, FromForm, UriDisplayQuery)]
@ -227,11 +240,39 @@ fn uri_display_serde() {
let bam = Bam {
foo: "hi[]=there.baz !?".into(),
bar: None,
baz: Ok("what is baz, anyway?".into()),
baz: true,
};
assert_query_form_roundtrip!(Bam, bam.clone());
assert_query_value_roundtrip!(JsonFoo, JsonFoo(Json(bam.clone())));
// FIXME: https://github.com/rust-lang/rust/issues/86706
#[allow(private_in_public)]
#[derive(Debug, PartialEq, Clone, FromForm, UriDisplayQuery)]
struct Q<T>(Json<T>);
#[derive(Debug, PartialEq, Clone, FromForm, UriDisplayQuery)]
pub struct Generic<A, B> {
a: Q<A>,
b: Q<B>,
c: Q<A>,
}
assert_query_form_roundtrip!(Generic<usize, String>, Generic {
a: Q(Json(133)),
b: Q(Json("hello, world#rocket!".into())),
c: Q(Json(40486)),
});
#[derive(Debug, PartialEq, Clone, FromForm, UriDisplayQuery)]
// This is here to ensure we don't warn, which we can't test with trybuild.
pub struct GenericBorrow<'a, A: ?Sized, B: 'a> {
a: Q<&'a A>,
b: Q<B>,
c: Q<&'a A>,
}
// TODO: This requires `MsgPack` to parse from value form fields.
//
// use rocket::serde::msgpack::MsgPack;