diff --git a/core/lib/src/data/from_data.rs b/core/lib/src/data/from_data.rs index 3eec2893..cd1fd8e1 100644 --- a/core/lib/src/data/from_data.rs +++ b/core/lib/src/data/from_data.rs @@ -415,12 +415,14 @@ impl<'r, T: FromData<'r> + 'r> FromData<'r> for Result { #[crate::async_trait] impl<'r, T: FromData<'r>> FromData<'r> for Option { - type Error = std::convert::Infallible; + type Error = T::Error; - async fn from_data(req: &'r Request<'_>, data: Data<'r>) -> Outcome<'r, Self> { - match T::from_data(req, data).await { - Success(v) => Success(Some(v)), - Error(..) | Forward(..) => Success(None), - } + async fn from_data(req: &'r Request<'_>, mut data: Data<'r>) -> Outcome<'r, Self> { + // Ask for at least one byte of data, if it's empty, there is no body. + if data.peek(1).await.is_empty() { + Outcome::Success(None) + } else { + T::from_data(req, data).await.map(Some) + } } } diff --git a/core/lib/src/form/from_form.rs b/core/lib/src/form/from_form.rs index 2f9d443d..6cb0923d 100644 --- a/core/lib/src/form/from_form.rs +++ b/core/lib/src/form/from_form.rs @@ -814,24 +814,51 @@ impl<'v, K, V> FromForm<'v> for BTreeMap } } +struct OptionFormCtx<'v, T: FromForm<'v>> { + strict: bool, + has_field: bool, + inner: T::Context, +} + #[crate::async_trait] impl<'v, T: FromForm<'v>> FromForm<'v> for Option { - type Context = >::Context; + type Context = OptionFormCtx<'v, T>; - fn init(_: Options) -> Self::Context { - T::init(Options { strict: true }) + fn init(opts: Options) -> Self::Context { + OptionFormCtx { + strict: opts.strict, + has_field: false, + inner: T::init(Options { strict: true }), + } } fn push_value(ctxt: &mut Self::Context, field: ValueField<'v>) { - T::push_value(ctxt, field) + ctxt.has_field = true; + T::push_value(&mut ctxt.inner, field) } async fn push_data(ctxt: &mut Self::Context, field: DataField<'v, '_>) { - T::push_data(ctxt, field).await + ctxt.has_field = true; + T::push_data(&mut ctxt.inner, field).await } fn finalize(this: Self::Context) -> Result<'v, Self> { - Ok(T::finalize(this).ok()) + if this.has_field { + match T::finalize(this.inner) { + Ok(v) => Ok(Some(v)), + Err(errors) => { + if this.strict || + errors.iter().any(|e| e.kind != ErrorKind::Missing) + { + Err(errors) + } else { + Ok(None) + } + } + } + } else { + Ok(None) + } } } diff --git a/core/lib/src/request/from_param.rs b/core/lib/src/request/from_param.rs index f639d38e..e0d62252 100644 --- a/core/lib/src/request/from_param.rs +++ b/core/lib/src/request/from_param.rs @@ -299,17 +299,17 @@ impl<'a, T: FromParam<'a>> FromParam<'a> for Result { } } -impl<'a, T: FromParam<'a>> FromParam<'a> for Option { - type Error = std::convert::Infallible; +// impl<'a, T: FromParam<'a>> FromParam<'a> for Option { +// type Error = std::convert::Infallible; - #[inline] - fn from_param(param: &'a str) -> Result { - match T::from_param(param) { - Ok(val) => Ok(Some(val)), - Err(_) => Ok(None) - } - } -} +// #[inline] +// fn from_param(param: &'a str) -> Result { +// match T::from_param(param) { +// Ok(val) => Ok(Some(val)), +// Err(_) => Ok(None) +// } +// } +// } /// Trait to convert _many_ dynamic path segment strings to a concrete value. /// @@ -384,13 +384,14 @@ impl<'r, T: FromSegments<'r>> FromSegments<'r> for Result { } impl<'r, T: FromSegments<'r>> FromSegments<'r> for Option { - type Error = std::convert::Infallible; + type Error = T::Error; #[inline] fn from_segments(segments: Segments<'r, Path>) -> Result, Self::Error> { - match T::from_segments(segments) { - Ok(val) => Ok(Some(val)), - Err(_) => Ok(None) + if segments.is_empty() { + Ok(None) + } else { + T::from_segments(segments).map(Some) } } }