Skip to content

Commit 29e7093

Browse files
committed
Impl 'FromForm' for char, 'Range' types.
Implements 'FromForm' for: * `char` * `Range<T: FromForm>` * `RangeFrom<T: FromForm>` * `RangeTo<T: FromForm>` * `RangeToInclusive<T: FromForm>` Resolves #2759.
1 parent 4c483dc commit 29e7093

File tree

5 files changed

+93
-26
lines changed

5 files changed

+93
-26
lines changed

core/lib/src/form/error.rs

+7
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::{fmt, io};
44
use std::num::{ParseIntError, ParseFloatError};
55
use std::str::{Utf8Error, ParseBoolError};
6+
use std::char::ParseCharError;
67
use std::net::AddrParseError;
78
use std::borrow::Cow;
89

@@ -200,6 +201,8 @@ pub enum ErrorKind<'v> {
200201
Multipart(multer::Error),
201202
/// A string was invalid UTF-8.
202203
Utf8(Utf8Error),
204+
/// A value failed to parse as a char.
205+
Char(ParseCharError),
203206
/// A value failed to parse as an integer.
204207
Int(ParseIntError),
205208
/// A value failed to parse as a boolean.
@@ -857,6 +860,7 @@ impl fmt::Display for ErrorKind<'_> {
857860
ErrorKind::Custom(_, e) => e.fmt(f)?,
858861
ErrorKind::Multipart(e) => write!(f, "invalid multipart: {}", e)?,
859862
ErrorKind::Utf8(e) => write!(f, "invalid UTF-8: {}", e)?,
863+
ErrorKind::Char(e) => write!(f, "invalid character: {}", e)?,
860864
ErrorKind::Int(e) => write!(f, "invalid integer: {}", e)?,
861865
ErrorKind::Bool(e) => write!(f, "invalid boolean: {}", e)?,
862866
ErrorKind::Float(e) => write!(f, "invalid float: {}", e)?,
@@ -885,6 +889,7 @@ impl crate::http::ext::IntoOwned for ErrorKind<'_> {
885889
Custom(s, e) => Custom(s, e),
886890
Multipart(e) => Multipart(e),
887891
Utf8(e) => Utf8(e),
892+
Char(e) => Char(e),
888893
Int(e) => Int(e),
889894
Bool(e) => Bool(e),
890895
Float(e) => Float(e),
@@ -985,6 +990,7 @@ macro_rules! impl_from_for {
985990

986991
impl_from_for!(<'a> Utf8Error => ErrorKind<'a> as Utf8);
987992
impl_from_for!(<'a> ParseIntError => ErrorKind<'a> as Int);
993+
impl_from_for!(<'a> ParseCharError => ErrorKind<'a> as Char);
988994
impl_from_for!(<'a> ParseFloatError => ErrorKind<'a> as Float);
989995
impl_from_for!(<'a> ParseBoolError => ErrorKind<'a> as Bool);
990996
impl_from_for!(<'a> AddrParseError => ErrorKind<'a> as Addr);
@@ -1024,6 +1030,7 @@ impl Entity {
10241030
| ErrorKind::OutOfRange { .. }
10251031
| ErrorKind::Validation { .. }
10261032
| ErrorKind::Utf8(_)
1033+
| ErrorKind::Char(_)
10271034
| ErrorKind::Int(_)
10281035
| ErrorKind::Float(_)
10291036
| ErrorKind::Bool(_)

core/lib/src/form/from_form.rs

+79-23
Original file line numberDiff line numberDiff line change
@@ -102,29 +102,33 @@ use crate::http::uncased::AsUncased;
102102
/// applications will never need a custom implementation of `FromForm` or
103103
/// `FromFormField`. Their behavior is documented in the table below.
104104
///
105-
/// | Type | Strategy | Default | Data | Value | Notes |
106-
/// |--------------------|-------------|-------------------|--------|--------|----------------------------------------------------|
107-
/// | [`Strict<T>`] | **strict** | if `strict` `T` | if `T` | if `T` | `T: FromForm` |
108-
/// | [`Lenient<T>`] | **lenient** | if `lenient` `T` | if `T` | if `T` | `T: FromForm` |
109-
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | Infallible, `T: FromForm` |
110-
/// | [`Result<T>`] | _inherit_ | `T::finalize()` | if `T` | if `T` | Infallible, `T: FromForm` |
111-
/// | `Vec<T>` | _inherit_ | `vec![]` | if `T` | if `T` | `T: FromForm` |
112-
/// | [`HashMap<K, V>`] | _inherit_ | `HashMap::new()` | if `V` | if `V` | `K: FromForm + Eq + Hash`, `V: FromForm` |
113-
/// | [`BTreeMap<K, V>`] | _inherit_ | `BTreeMap::new()` | if `V` | if `V` | `K: FromForm + Ord`, `V: FromForm` |
114-
/// | `bool` | _inherit_ | `false` | No | Yes | `"yes"/"on"/"true"`, `"no"/"off"/"false"` |
115-
/// | (un)signed int | _inherit_ | **no default** | No | Yes | `{u,i}{size,8,16,32,64,128}` |
116-
/// | _nonzero_ int | _inherit_ | **no default** | No | Yes | `NonZero{I,U}{size,8,16,32,64,128}` |
117-
/// | float | _inherit_ | **no default** | No | Yes | `f{32,64}` |
118-
/// | `&str` | _inherit_ | **no default** | Yes | Yes | Percent-decoded. Data limit `string` applies. |
119-
/// | `&[u8]` | _inherit_ | **no default** | Yes | Yes | Raw bytes. Data limit `bytes` applies. |
120-
/// | `String` | _inherit_ | **no default** | Yes | Yes | Exactly `&str`, but owned. Prefer `&str`. |
121-
/// | IP Address | _inherit_ | **no default** | No | Yes | [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`] |
122-
/// | Socket Address | _inherit_ | **no default** | No | Yes | [`SocketAddr`], [`SocketAddrV4`], [`SocketAddrV6`] |
123-
/// | [`TempFile`] | _inherit_ | **no default** | Yes | Yes | Data limits apply. See [`TempFile`]. |
124-
/// | [`Capped<C>`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, `&[u8]` or `TempFile`. |
125-
/// | [`time::Date`] | _inherit_ | **no default** | No | Yes | `%F` (`YYYY-MM-DD`). HTML "date" input. |
126-
/// | [`time::DateTime`] | _inherit_ | **no default** | No | Yes | `%FT%R` or `%FT%T` (`YYYY-MM-DDTHH:MM[:SS]`) |
127-
/// | [`time::Time`] | _inherit_ | **no default** | No | Yes | `%R` or `%T` (`HH:MM[:SS]`) |
105+
/// | Type | Strategy | Default | Data | Value | Notes |
106+
/// |------------------------|-------------|-------------------|--------|--------|----------------------------------------------------|
107+
/// | [`Strict<T>`] | **strict** | if `strict` `T` | if `T` | if `T` | `T: FromForm` |
108+
/// | [`Lenient<T>`] | **lenient** | if `lenient` `T` | if `T` | if `T` | `T: FromForm` |
109+
/// | `Option<T>` | **strict** | `None` | if `T` | if `T` | Infallible, `T: FromForm` |
110+
/// | [`Result<T>`] | _inherit_ | `T::finalize()` | if `T` | if `T` | Infallible, `T: FromForm` |
111+
/// | `Vec<T>` | _inherit_ | `vec![]` | if `T` | if `T` | `T: FromForm` |
112+
/// | [`HashMap<K, V>`] | _inherit_ | `HashMap::new()` | if `V` | if `V` | `K: FromForm + Eq + Hash`, `V: FromForm` |
113+
/// | [`BTreeMap<K, V>`] | _inherit_ | `BTreeMap::new()` | if `V` | if `V` | `K: FromForm + Ord`, `V: FromForm` |
114+
/// | [`Range<T>`] | _inherit_ | **no default** | if `T` | if `T` | `T: FromForm`, expects `start`, `end` fields |
115+
/// | [`RangeFrom<T>`] | _inherit_ | **no default** | if `T` | if `T` | `T: FromForm`, expects `start` field |
116+
/// | [`RangeTo<T>`] | _inherit_ | **no default** | if `T` | if `T` | `T: FromForm`, expects `end` field |
117+
/// | [`RangeToInclusive<T>`]| _inherit_ | **no default** | if `T` | if `T` | `T: FromForm`, expects `end` field |
118+
/// | `bool` | _inherit_ | `false` | No | Yes | `"yes"/"on"/"true"`, `"no"/"off"/"false"` |
119+
/// | (un)signed int | _inherit_ | **no default** | No | Yes | `{u,i}{size,8,16,32,64,128}` |
120+
/// | _nonzero_ int | _inherit_ | **no default** | No | Yes | `NonZero{I,U}{size,8,16,32,64,128}` |
121+
/// | float | _inherit_ | **no default** | No | Yes | `f{32,64}` |
122+
/// | `&str` | _inherit_ | **no default** | Yes | Yes | Percent-decoded. Data limit `string` applies. |
123+
/// | `&[u8]` | _inherit_ | **no default** | Yes | Yes | Raw bytes. Data limit `bytes` applies. |
124+
/// | `String` | _inherit_ | **no default** | Yes | Yes | Exactly `&str`, but owned. Prefer `&str`. |
125+
/// | IP Address | _inherit_ | **no default** | No | Yes | [`IpAddr`], [`Ipv4Addr`], [`Ipv6Addr`] |
126+
/// | Socket Address | _inherit_ | **no default** | No | Yes | [`SocketAddr`], [`SocketAddrV4`], [`SocketAddrV6`] |
127+
/// | [`TempFile`] | _inherit_ | **no default** | Yes | Yes | Data limits apply. See [`TempFile`]. |
128+
/// | [`Capped<C>`] | _inherit_ | **no default** | Yes | Yes | `C` is `&str`, `String`, `&[u8]` or `TempFile`. |
129+
/// | [`time::Date`] | _inherit_ | **no default** | No | Yes | `%F` (`YYYY-MM-DD`). HTML "date" input. |
130+
/// | [`time::DateTime`] | _inherit_ | **no default** | No | Yes | `%FT%R` or `%FT%T` (`YYYY-MM-DDTHH:MM[:SS]`) |
131+
/// | [`time::Time`] | _inherit_ | **no default** | No | Yes | `%R` or `%T` (`HH:MM[:SS]`) |
128132
///
129133
/// [`Result<T>`]: crate::form::Result
130134
/// [`Strict<T>`]: crate::form::Strict
@@ -140,6 +144,10 @@ use crate::http::uncased::AsUncased;
140144
/// [`SocketAddr`]: std::net::SocketAddr
141145
/// [`SocketAddrV4`]: std::net::SocketAddrV4
142146
/// [`SocketAddrV6`]: std::net::SocketAddrV6
147+
/// [`Range<T>`]: https://doc.rust-lang.org/stable/std/ops/struct.Range.html
148+
/// [`RangeFrom<T>`]: https://doc.rust-lang.org/stable/std/ops/struct.RangeFrom.html
149+
/// [`RangeTo<T>`]: https://doc.rust-lang.org/stable/std/ops/struct.RangeTo.html
150+
/// [`RangeToInclusive<T>`]: https://doc.rust-lang.org/stable/std/ops/struct.RangeToInclusive.html
143151
///
144152
/// ## Additional Notes
145153
///
@@ -931,3 +939,51 @@ impl<'v, T: FromForm<'v> + Sync> FromForm<'v> for Arc<T> {
931939
T::finalize(this).map(Arc::new)
932940
}
933941
}
942+
943+
macro_rules! impl_via_proxy {
944+
($R:ident => struct $T:ident <$($G:ident),*> { $($f:ident : $F:ident),* }) => {
945+
const _: () = {
946+
use super::*;
947+
948+
mod proxy {
949+
#[derive(rocket::FromForm)]
950+
pub struct $T<$($G),*> {
951+
$(pub $f : $F),*
952+
}
953+
}
954+
955+
#[crate::async_trait]
956+
impl<'v, $($G: Send),*> FromForm<'v> for $R<$($G),*>
957+
where proxy::$T<$($G),*>: FromForm<'v>
958+
{
959+
type Context = <proxy::$T<$($G),*> as FromForm<'v>>::Context;
960+
961+
fn init(opts: Options) -> Self::Context {
962+
<proxy::$T<$($G),*>>::init(opts)
963+
}
964+
965+
fn push_value(ctxt: &mut Self::Context, field: ValueField<'v>) {
966+
<proxy::$T<$($G),*>>::push_value(ctxt, field)
967+
}
968+
969+
async fn push_data(ctxt: &mut Self::Context, field: DataField<'v, '_>) {
970+
<proxy::$T<$($G),*>>::push_data(ctxt, field).await
971+
}
972+
973+
fn finalize(this: Self::Context) -> Result<'v, Self> {
974+
let proxy = <proxy::$T<$($G),*>>::finalize(this)?;
975+
Ok($R {
976+
$($f : proxy.$f),*
977+
})
978+
}
979+
}
980+
};
981+
}
982+
}
983+
984+
use std::ops::{Range, RangeFrom, RangeTo, RangeToInclusive};
985+
986+
impl_via_proxy!(Range => struct Range<T> { start: T, end: T });
987+
impl_via_proxy!(RangeFrom => struct RangeFrom<T> { start: T });
988+
impl_via_proxy!(RangeTo => struct RangeTo<T> { end: T });
989+
impl_via_proxy!(RangeToInclusive => struct RangeToInclusive<T> { end: T });

core/lib/src/form/from_form_field.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -391,14 +391,15 @@ macro_rules! impl_with_parse {
391391
}
392392

393393
impl_with_parse!(
394+
char,
394395
f32, f64,
395396
isize, i8, i16, i32, i64, i128,
396397
usize, u8, u16, u32, u64, u128,
397398
NonZeroIsize, NonZeroI8, NonZeroI16, NonZeroI32, NonZeroI64, NonZeroI128,
398399
NonZeroUsize, NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128,
399400
Ipv4Addr, IpAddr, Ipv6Addr, SocketAddrV4, SocketAddrV6, SocketAddr
400401
);
401-
//
402+
402403
// Keep formats in sync with 'FromFormField' impls.
403404
static DATE_FMT: &[FormatItem<'_>] = format_description!("[year padding:none]-[month]-[day]");
404405
static TIME_FMT1: &[FormatItem<'_>] = format_description!("[hour padding:none]:[minute]:[second]");

core/lib/src/lib.rs

+5-2
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@
107107
//! [testing guide]: https://rocket.rs/v0.5/guide/testing/#testing
108108
//! [Figment]: https://docs.rs/figment
109109
110+
// Allows using Rocket's codegen in Rocket itself.
111+
extern crate self as rocket;
112+
110113
/// These are public dependencies! Update docs if these are changed, especially
111114
/// figment's version number in docs.
112115
#[doc(hidden)] pub use yansi;
@@ -171,7 +174,7 @@ mod server;
171174
mod ext;
172175
mod state;
173176
mod cookies;
174-
mod rocket;
177+
mod rkt;
175178
mod router;
176179
mod phase;
177180

@@ -185,7 +188,7 @@ mod phase;
185188
#[doc(inline)] pub use error::Error;
186189
#[doc(inline)] pub use sentinel::Sentinel;
187190
#[doc(inline)] pub use crate::request::Request;
188-
#[doc(inline)] pub use crate::rocket::Rocket;
191+
#[doc(inline)] pub use crate::rkt::Rocket;
189192
#[doc(inline)] pub use crate::shutdown::Shutdown;
190193
#[doc(inline)] pub use crate::state::State;
191194
#[doc(inline)] pub use rocket_codegen::*;
File renamed without changes.

0 commit comments

Comments
 (0)