diff --git a/core/codegen/src/derive/form_field.rs b/core/codegen/src/derive/form_field.rs index 60688e5efb..ac3119d63a 100644 --- a/core/codegen/src/derive/form_field.rs +++ b/core/codegen/src/derive/form_field.rs @@ -9,6 +9,14 @@ use quote::{ToTokens, TokenStreamExt}; use crate::syn_ext::IdentExt; use crate::name::Name; +macro_rules! quote_spanned { + ($span:expr => $($token:tt)*) => ( + quote::quote_spanned!( + proc_macro2::Span::call_site().located_at($span) => $($token)* + ) + ) +} + #[derive(Debug)] pub enum FieldName { Cased(Name), diff --git a/core/codegen/src/derive/from_form.rs b/core/codegen/src/derive/from_form.rs index f1ef4f50d0..63aff1362d 100644 --- a/core/codegen/src/derive/from_form.rs +++ b/core/codegen/src/derive/from_form.rs @@ -9,6 +9,14 @@ use crate::syn_ext::{GenericsExt as _, TypeExt as _}; type WherePredicates = syn::punctuated::Punctuated; +macro_rules! quote_spanned { + ($span:expr => $($token:tt)*) => ( + quote::quote_spanned!( + proc_macro2::Span::call_site().located_at($span) => $($token)* + ) + ) +} + // F: fn(field_ty: Ty, field_context: Expr) fn fields_map(fields: Fields<'_>, map_f: F) -> Result where F: Fn(&syn::Type, &syn::Expr) -> TokenStream diff --git a/core/codegen/tests/from_form.rs b/core/codegen/tests/from_form.rs index 568840d191..bed73596db 100644 --- a/core/codegen/tests/from_form.rs +++ b/core/codegen/tests/from_form.rs @@ -991,3 +991,50 @@ struct Q(T); // This is here to ensure we don't warn, which we can't test with trybuild. #[derive(FromForm)] pub struct JsonTokenBad(Q); + +#[test] +fn range() { + use std::ops::{Range, RangeFrom, RangeTo, RangeToInclusive}; + + let range: Range = strict("start=1&end=2").unwrap(); + assert_eq!(range, Range { start: 1, end: 2 }); + + let range: Range = strict_encoded("start=-1&end=2").unwrap(); + assert_eq!(range, Range { start: -1, end: 2 }); + + let range: Range = strict("start=-1&end=-95").unwrap(); + assert_eq!(range, Range { start: -1, end: -95 }); + + let range: Range = strict_encoded("start=-1&end=-95").unwrap(); + assert_eq!(range, Range { start: -1, end: -95 }); + + let range: RangeFrom = strict("start=a").unwrap(); + assert_eq!(range, RangeFrom { start: 'a' }); + + let range: RangeTo = strict("end=z").unwrap(); + assert_eq!(range, RangeTo { end: 'z' }); + + let range: RangeToInclusive = strict("end=255").unwrap(); + assert_eq!(range, RangeToInclusive { end: 255 }); + + // now with compound, non-Step values + let form_string = &["start.start=0", "start.end=1", "end.start=1", "end.end=2"].join("&"); + let range: Range> = strict(&form_string).unwrap(); + assert_eq!(range, Range { + start: Range { start: 0, end : 1}, + end: Range { start: 1, end : 2 } + }); + + let form_string = &[ + "start.description=some%20task", + "start.completed=false", + "end.description=yet%20more%20work", + "end.completed=true", + ].join("&"); + + let range: Range = strict_encoded(form_string).unwrap(); + assert_eq!(range, Range { + start: TodoTask { description: "some task".into(), completed: false, }, + end: TodoTask { description: "yet more work".into(), completed: true, }, + }); +} diff --git a/core/lib/src/form/error.rs b/core/lib/src/form/error.rs index d6a42a5563..b2c2c06e30 100644 --- a/core/lib/src/form/error.rs +++ b/core/lib/src/form/error.rs @@ -3,6 +3,7 @@ use std::{fmt, io}; use std::num::{ParseIntError, ParseFloatError}; use std::str::{Utf8Error, ParseBoolError}; +use std::char::ParseCharError; use std::net::AddrParseError; use std::borrow::Cow; @@ -200,6 +201,8 @@ pub enum ErrorKind<'v> { Multipart(multer::Error), /// A string was invalid UTF-8. Utf8(Utf8Error), + /// A value failed to parse as a char. + Char(ParseCharError), /// A value failed to parse as an integer. Int(ParseIntError), /// A value failed to parse as a boolean. @@ -857,6 +860,7 @@ impl fmt::Display for ErrorKind<'_> { ErrorKind::Custom(_, e) => e.fmt(f)?, ErrorKind::Multipart(e) => write!(f, "invalid multipart: {}", e)?, ErrorKind::Utf8(e) => write!(f, "invalid UTF-8: {}", e)?, + ErrorKind::Char(e) => write!(f, "invalid character: {}", e)?, ErrorKind::Int(e) => write!(f, "invalid integer: {}", e)?, ErrorKind::Bool(e) => write!(f, "invalid boolean: {}", e)?, ErrorKind::Float(e) => write!(f, "invalid float: {}", e)?, @@ -885,6 +889,7 @@ impl crate::http::ext::IntoOwned for ErrorKind<'_> { Custom(s, e) => Custom(s, e), Multipart(e) => Multipart(e), Utf8(e) => Utf8(e), + Char(e) => Char(e), Int(e) => Int(e), Bool(e) => Bool(e), Float(e) => Float(e), @@ -985,6 +990,7 @@ macro_rules! impl_from_for { impl_from_for!(<'a> Utf8Error => ErrorKind<'a> as Utf8); impl_from_for!(<'a> ParseIntError => ErrorKind<'a> as Int); +impl_from_for!(<'a> ParseCharError => ErrorKind<'a> as Char); impl_from_for!(<'a> ParseFloatError => ErrorKind<'a> as Float); impl_from_for!(<'a> ParseBoolError => ErrorKind<'a> as Bool); impl_from_for!(<'a> AddrParseError => ErrorKind<'a> as Addr); @@ -1024,6 +1030,7 @@ impl Entity { | ErrorKind::OutOfRange { .. } | ErrorKind::Validation { .. } | ErrorKind::Utf8(_) + | ErrorKind::Char(_) | ErrorKind::Int(_) | ErrorKind::Float(_) | ErrorKind::Bool(_) diff --git a/core/lib/src/form/from_form.rs b/core/lib/src/form/from_form.rs index 7bf7914848..55dec5a137 100644 --- a/core/lib/src/form/from_form.rs +++ b/core/lib/src/form/from_form.rs @@ -931,3 +931,51 @@ impl<'v, T: FromForm<'v> + Sync> FromForm<'v> for Arc { T::finalize(this).map(Arc::new) } } + +macro_rules! impl_via_proxy { + ($R:ident => struct $T:ident <$($G:ident),*> { $($f:ident : $F:ident),* }) => { + const _: () = { + use super::*; + + mod proxy { + #[derive(rocket::FromForm)] + pub struct $T<$($G),*> { + $(pub $f : $F),* + } + } + + #[crate::async_trait] + impl<'v, $($G: Send),*> FromForm<'v> for $R<$($G),*> + where proxy::$T<$($G),*>: FromForm<'v> + { + type Context = as FromForm<'v>>::Context; + + fn init(opts: Options) -> Self::Context { + >::init(opts) + } + + fn push_value(ctxt: &mut Self::Context, field: ValueField<'v>) { + >::push_value(ctxt, field) + } + + async fn push_data(ctxt: &mut Self::Context, field: DataField<'v, '_>) { + >::push_data(ctxt, field).await + } + + fn finalize(this: Self::Context) -> Result<'v, Self> { + let proxy = >::finalize(this)?; + Ok($R { + $($f : proxy.$f),* + }) + } + } + }; + } +} + +use std::ops::{Range, RangeFrom, RangeTo, RangeToInclusive}; + +impl_via_proxy!(Range => struct Range { start: T, end: T }); +impl_via_proxy!(RangeFrom => struct RangeFrom { start: T }); +impl_via_proxy!(RangeTo => struct RangeTo { end: T }); +impl_via_proxy!(RangeToInclusive => struct RangeToInclusive { end: T }); diff --git a/core/lib/src/form/from_form_field.rs b/core/lib/src/form/from_form_field.rs index 7e733133cf..2a7f5ab22b 100644 --- a/core/lib/src/form/from_form_field.rs +++ b/core/lib/src/form/from_form_field.rs @@ -391,6 +391,7 @@ macro_rules! impl_with_parse { } impl_with_parse!( + char, f32, f64, isize, i8, i16, i32, i64, i128, usize, u8, u16, u32, u64, u128, @@ -398,7 +399,7 @@ impl_with_parse!( NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, Ipv4Addr, IpAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr ); -// + // Keep formats in sync with 'FromFormField' impls. static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]"); static TIME_FMT1: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]"); diff --git a/core/lib/src/lib.rs b/core/lib/src/lib.rs index bbf10cc36b..b372ac0d8b 100644 --- a/core/lib/src/lib.rs +++ b/core/lib/src/lib.rs @@ -107,6 +107,8 @@ //! [testing guide]: https://rocket.rs/master/guide/testing/#testing //! [Figment]: https://docs.rs/figment +extern crate self as rocket; + /// These are public dependencies! Update docs if these are changed, especially /// figment's version number in docs. #[doc(hidden)] @@ -153,11 +155,13 @@ mod util; mod server; mod lifecycle; mod state; -mod rocket; mod router; mod phase; mod erased; +#[path = "rocket.rs"] +mod rkt; + #[doc(hidden)] pub use either::Either; #[doc(inline)] pub use rocket_codegen::*; @@ -171,7 +175,7 @@ mod erased; #[doc(inline)] pub use crate::error::Error; #[doc(inline)] pub use crate::sentinel::Sentinel; #[doc(inline)] pub use crate::request::Request; -#[doc(inline)] pub use crate::rocket::Rocket; +#[doc(inline)] pub use crate::rkt::Rocket; #[doc(inline)] pub use crate::shutdown::Shutdown; #[doc(inline)] pub use crate::state::State;