Skip to content

Commit a6b2a05

Browse files
authored
Merge pull request #262 from mfelsche/serialize
Add custom deserialize and serialize impls for Version, StatusCode and Method
2 parents 18b86df + ad513b5 commit a6b2a05

File tree

3 files changed

+209
-0
lines changed

3 files changed

+209
-0
lines changed

src/method.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use serde::de::{Error as DeError, Unexpected};
2+
use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
13
use std::fmt::{self, Display};
24
use std::str::FromStr;
35

@@ -50,6 +52,44 @@ impl Method {
5052
}
5153
}
5254

55+
struct MethodVisitor;
56+
57+
impl<'de> Visitor<'de> for MethodVisitor {
58+
type Value = Method;
59+
60+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
61+
write!(formatter, "a HTTP method &str")
62+
}
63+
64+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
65+
where
66+
E: DeError,
67+
{
68+
match Method::from_str(v) {
69+
Ok(method) => Ok(method),
70+
Err(_) => Err(DeError::invalid_value(Unexpected::Str(v), &self)),
71+
}
72+
}
73+
}
74+
75+
impl<'de> Deserialize<'de> for Method {
76+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
77+
where
78+
D: Deserializer<'de>,
79+
{
80+
deserializer.deserialize_str(MethodVisitor)
81+
}
82+
}
83+
84+
impl Serialize for Method {
85+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
86+
where
87+
S: Serializer,
88+
{
89+
serializer.serialize_str(&self.to_string())
90+
}
91+
}
92+
5393
impl Display for Method {
5494
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
5595
match self {
@@ -108,3 +148,20 @@ impl AsRef<str> for Method {
108148
}
109149
}
110150
}
151+
152+
#[cfg(test)]
153+
mod test {
154+
use super::Method;
155+
156+
#[test]
157+
fn serde() -> Result<(), serde_json::Error> {
158+
assert_eq!(Method::Get, serde_json::from_str("\"GET\"")?);
159+
assert_eq!(Some("PATCH"), serde_json::to_value(Method::Patch)?.as_str());
160+
Ok(())
161+
}
162+
#[test]
163+
fn serde_fail() -> Result<(), serde_json::Error> {
164+
serde_json::from_str::<Method>("\"ABC\"").expect_err("Did deserialize from invalid string");
165+
Ok(())
166+
}
167+
}

src/status_code.rs

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use serde::de::{Error as DeError, Unexpected, Visitor};
2+
use serde::{Deserialize, Deserializer, Serialize, Serializer};
13
use std::fmt::{self, Display};
24

35
/// HTTP response status codes.
@@ -537,6 +539,84 @@ impl StatusCode {
537539
}
538540
}
539541

542+
impl Serialize for StatusCode {
543+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
544+
where
545+
S: Serializer,
546+
{
547+
let value: u16 = *self as u16;
548+
serializer.serialize_u16(value)
549+
}
550+
}
551+
552+
struct StatusCodeU16Visitor;
553+
554+
impl<'de> Visitor<'de> for StatusCodeU16Visitor {
555+
type Value = StatusCode;
556+
557+
fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
558+
write!(formatter, "a u16 representing the status code")
559+
}
560+
561+
fn visit_i16<E>(self, v: i16) -> Result<Self::Value, E>
562+
where
563+
E: DeError,
564+
{
565+
self.visit_u16(v as u16)
566+
}
567+
568+
fn visit_i32<E>(self, v: i32) -> Result<Self::Value, E>
569+
where
570+
E: DeError,
571+
{
572+
self.visit_u16(v as u16)
573+
}
574+
575+
fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
576+
where
577+
E: DeError,
578+
{
579+
self.visit_u16(v as u16)
580+
}
581+
582+
fn visit_u16<E>(self, v: u16) -> Result<Self::Value, E>
583+
where
584+
E: DeError,
585+
{
586+
use std::convert::TryFrom;
587+
match StatusCode::try_from(v) {
588+
Ok(status_code) => Ok(status_code),
589+
Err(_) => Err(DeError::invalid_value(
590+
Unexpected::Unsigned(v as u64),
591+
&self,
592+
)),
593+
}
594+
}
595+
596+
fn visit_u32<E>(self, v: u32) -> Result<Self::Value, E>
597+
where
598+
E: DeError,
599+
{
600+
self.visit_u16(v as u16)
601+
}
602+
603+
fn visit_u64<E>(self, v: u64) -> Result<Self::Value, E>
604+
where
605+
E: DeError,
606+
{
607+
self.visit_u16(v as u16)
608+
}
609+
}
610+
611+
impl<'de> Deserialize<'de> for StatusCode {
612+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
613+
where
614+
D: Deserializer<'de>,
615+
{
616+
deserializer.deserialize_any(StatusCodeU16Visitor)
617+
}
618+
}
619+
540620
impl From<StatusCode> for u16 {
541621
fn from(code: StatusCode) -> u16 {
542622
code as u16
@@ -629,3 +709,18 @@ impl Display for StatusCode {
629709
write!(f, "{}", *self as u16)
630710
}
631711
}
712+
713+
#[cfg(test)]
714+
mod test {
715+
use super::StatusCode;
716+
#[test]
717+
fn serde_as_u16() -> Result<(), serde_json::Error> {
718+
let status_code: StatusCode = serde_json::from_str("202")?;
719+
assert_eq!(StatusCode::Accepted, status_code);
720+
assert_eq!(
721+
Some(202),
722+
serde_json::to_value(&StatusCode::Accepted)?.as_u64()
723+
);
724+
Ok(())
725+
}
726+
}

src/version.rs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use serde::{de::Error, de::Visitor, Deserialize, Deserializer, Serialize, Serializer};
12
/// The version of the HTTP protocol in use.
23
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
34
#[non_exhaustive]
@@ -18,6 +19,55 @@ pub enum Version {
1819
Http3_0,
1920
}
2021

22+
impl Serialize for Version {
23+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
24+
where
25+
S: Serializer,
26+
{
27+
serializer.serialize_str(&self.to_string())
28+
}
29+
}
30+
31+
struct VersionVisitor;
32+
33+
impl<'de> Visitor<'de> for VersionVisitor {
34+
type Value = Version;
35+
36+
fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
37+
write!(formatter, "a HTTP version as &str")
38+
}
39+
40+
fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
41+
where
42+
E: Error,
43+
{
44+
match v {
45+
"HTTP/0.9" => Ok(Version::Http0_9),
46+
"HTTP/1.0" => Ok(Version::Http1_0),
47+
"HTTP/1.1" => Ok(Version::Http1_1),
48+
"HTTP/2" => Ok(Version::Http2_0),
49+
"HTTP/3" => Ok(Version::Http3_0),
50+
_ => Err(Error::invalid_value(serde::de::Unexpected::Str(v), &self)),
51+
}
52+
}
53+
54+
fn visit_string<E>(self, v: String) -> Result<Self::Value, E>
55+
where
56+
E: Error,
57+
{
58+
self.visit_str(&v)
59+
}
60+
}
61+
62+
impl<'de> Deserialize<'de> for Version {
63+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
64+
where
65+
D: Deserializer<'de>,
66+
{
67+
deserializer.deserialize_str(VersionVisitor)
68+
}
69+
}
70+
2171
impl std::fmt::Display for Version {
2272
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2373
f.write_str(match self {
@@ -54,4 +104,11 @@ mod test {
54104
assert!(Http1_1 > Http1_0);
55105
assert!(Http1_0 > Http0_9);
56106
}
107+
108+
#[test]
109+
fn serde() -> Result<(), serde_json::Error> {
110+
assert_eq!("\"HTTP/3\"", serde_json::to_string(&Version::Http3_0)?);
111+
assert_eq!(Version::Http1_1, serde_json::from_str("\"HTTP/1.1\"")?);
112+
Ok(())
113+
}
57114
}

0 commit comments

Comments
 (0)