1use std::fmt::{self, Display};
2use std::cmp::Ordering;
3use std::io::{Error, ErrorKind};
4use std::str::FromStr;
5
6#[derive(Debug, Clone, Copy, Eq, Hash)]
7pub enum Version {
8 Http0_9 = 9,
9 Http1_0 = 10,
10 Http1_1 = 11,
11 Http2_0 = 20,
12 Http3_0 = 30,
13}
14
15impl Display for Version {
16
17 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
18 match self {
19 Self::Http0_9 => write!(f, "HTTP/0.9"),
20 Self::Http1_0 => write!(f, "HTTP/1.0"),
21 Self::Http1_1 => write!(f, "HTTP/1.1"),
22 Self::Http2_0 => write!(f, "HTTP/2"),
23 Self::Http3_0 => write!(f, "HTTP/3"),
24 }
25 }
26}
27
28impl FromStr for Version {
29 type Err = Error;
30
31 fn from_str(v: &str) -> Result<Self, Self::Err> {
32 match v {
33 "HTTP/0.9" => Ok(Self::Http0_9),
34 "0.9" => Ok(Self::Http0_9),
35 "HTTP/1.0" => Ok(Self::Http1_0),
36 "1.0" => Ok(Self::Http1_0),
37 "HTTP/1.1" => Ok(Self::Http1_1),
38 "1.1" => Ok(Self::Http1_1),
39 "1" => Ok(Self::Http1_1),
40 "HTTP/2" => Ok(Self::Http2_0),
41 "2.0" => Ok(Self::Http2_0),
42 "2" => Ok(Self::Http2_0),
43 "HTTP/3" => Ok(Self::Http3_0),
44 "3.0" => Ok(Self::Http3_0),
45 "3" => Ok(Self::Http3_0),
46 v => Err(Error::new(ErrorKind::InvalidInput, format!("The version `{}` is invalid.", v))),
47 }
48 }
49}
50
51impl<'a> std::convert::TryFrom<&[u8]> for Version {
52 type Error = Error;
53
54 fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
55 match String::from_utf8(bytes.to_vec()) {
56 Ok(txt) => Self::from_str(&txt),
57 Err(e) => Err(Error::new(ErrorKind::InvalidInput, e.to_string())),
58 }
59 }
60}
61
62impl PartialOrd for Version {
63 fn partial_cmp(&self, other: &Version) -> Option<Ordering> {
64 Some(self.cmp(other))
65 }
66}
67
68impl Ord for Version {
69 fn cmp(&self, other: &Version) -> Ordering {
70 (*self as usize).cmp(&(*other as usize))
71 }
72}
73
74impl PartialEq for Version {
75 fn eq(&self, other: &Version) -> bool {
76 (*self as usize) == (*other as usize)
77 }
78}
79
80#[cfg(test)]
81mod tests {
82 use super::*;
83 use std::convert::TryFrom;
84
85 #[test]
86 fn implements_from_str() {
87 let version = Version::from_str("1.1").unwrap();
88 assert_eq!(version, Version::Http1_1);
89 }
90
91 #[test]
92 fn implements_try_from() {
93 let version = Version::try_from("1.1".as_bytes()).unwrap();
94 assert_eq!(version, Version::Http1_1);
95 }
96
97 #[test]
98 fn implements_to_string() {
99 let version = Version::from_str("2.0").unwrap();
100 assert_eq!(version.to_string(), "HTTP/2");
101 }
102
103 #[test]
104 fn implements_ordering() {
105 assert!(Version::Http1_1 > Version::Http0_9);
106 assert!(Version::Http0_9 < Version::Http1_0);
107 assert!(Version::Http1_0 == Version::Http1_0);
108 }
109}