Implement 'De(Serialize)' for 'Status'.

Resolves #2366.
This commit is contained in:
Sergio Benitez 2023-03-26 19:18:04 -07:00
parent 8f2ee138ea
commit a474fde85b
2 changed files with 124 additions and 1 deletions

View File

@ -92,6 +92,26 @@ impl StatusClass {
/// ```
///
/// [`response::status`]: ../response/status/index.html
///
/// # (De)serialization
///
/// `Status` is both `Serialize` and `Deserialize`, represented as a `u16`. For
/// example, [`Status::Ok`] (de)serializes from/to `200`. Any integer in the
/// range `[100, 600)` is allowed to deserialize into a `Status`.`
///
/// ```rust
/// # #[cfg(feature = "serde")] mod serde {
/// # use serde_ as serde;
/// use serde::{Serialize, Deserialize};
/// use rocket::http::Status;
///
/// #[derive(Deserialize, Serialize)]
/// # #[serde(crate = "serde_")]
/// struct Foo {
/// status: Status,
/// }
/// # }
/// ```
#[derive(Debug, Clone, Copy)]
pub struct Status {
/// The HTTP status code associated with this status.
@ -359,3 +379,50 @@ impl Ord for Status {
self.code.cmp(&other.code)
}
}
#[cfg(feature = "serde")]
mod serde {
use std::fmt;
use super::*;
use serde_::ser::{Serialize, Serializer};
use serde_::de::{Deserialize, Deserializer, Error, Visitor, Unexpected};
impl<'a> Serialize for Status {
fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u16(self.code)
}
}
struct DeVisitor;
impl<'de> Visitor<'de> for DeVisitor {
type Value = Status;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "HTTP status code integer in range [100, 600)")
}
fn visit_i64<E: Error>(self, v: i64) -> Result<Self::Value, E> {
if v < 100 || v >= 600 {
return Err(E::invalid_value(Unexpected::Signed(v), &self));
}
Ok(Status::new(v as u16))
}
fn visit_u64<E: Error>(self, v: u64) -> Result<Self::Value, E> {
if v < 100 || v >= 600 {
return Err(E::invalid_value(Unexpected::Unsigned(v), &self));
}
Ok(Status::new(v as u16))
}
}
impl<'de> Deserialize<'de> for Status {
fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
deserializer.deserialize_u16(DeVisitor)
}
}
}

View File

@ -4,7 +4,7 @@ use pretty_assertions::assert_eq;
use rocket::{Config, uri};
use rocket::http::uri::{Absolute, Asterisk, Authority, Origin, Reference};
use rocket::http::Method;
use rocket::http::{Method, Status};
#[derive(PartialEq, Debug, Serialize, Deserialize)]
struct UriContainer<'a> {
@ -31,6 +31,13 @@ struct MethodContainer {
mpost: Method,
}
#[derive(PartialEq, Debug, Serialize, Deserialize)]
struct StatusContainer {
a: Status,
b: Status,
c: Status,
}
#[test]
fn uri_serde() {
figment::Jail::expect_with(|jail| {
@ -148,3 +155,52 @@ fn method_serde() {
Ok(())
});
}
#[test]
fn status_serde() {
figment::Jail::expect_with(|jail| {
jail.create_file("Rocket.toml", r#"
[default]
a = 500
b = 100
c = 404
"#)?;
let statuses: StatusContainer = Config::figment().extract()?;
assert_eq!(statuses, StatusContainer {
a: Status::InternalServerError,
b: Status::Continue,
c: Status::NotFound
});
let tmp = Figment::from(Serialized::defaults(statuses));
let statuses: StatusContainer = tmp.extract()?;
assert_eq!(statuses, StatusContainer {
a: Status::InternalServerError,
b: Status::Continue,
c: Status::NotFound
});
jail.create_file("Rocket.toml", r#"
[default]
a = 99
b = 100
c = 404
"#)?;
let statuses: Result<StatusContainer, _> = Config::figment().extract();
assert!(statuses.is_err());
jail.create_file("Rocket.toml", r#"
[default]
a = 500
b = 100
c = 600
"#)?;
let statuses: Result<StatusContainer, _> = Config::figment().extract();
assert!(statuses.is_err());
Ok(())
});
}