actix_web_lab/
content_length.rs1use std::{convert::Infallible, str};
6
7use actix_web::{
8 error::ParseError,
9 http::header::{
10 from_one_raw_str, Header, HeaderName, HeaderValue, TryIntoHeaderValue, CONTENT_LENGTH,
11 },
12 HttpMessage,
13};
14
15#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
27pub struct ContentLength(usize);
28
29impl ContentLength {
30 pub fn into_inner(&self) -> usize {
32 self.0
33 }
34}
35
36impl str::FromStr for ContentLength {
37 type Err = <usize as str::FromStr>::Err;
38
39 #[inline]
40 fn from_str(val: &str) -> Result<Self, Self::Err> {
41 let val = val.trim();
42
43 debug_assert!(!val.starts_with('+'));
45
46 val.parse().map(Self)
47 }
48}
49
50impl TryIntoHeaderValue for ContentLength {
51 type Error = Infallible;
52
53 fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
54 Ok(HeaderValue::from(self.0))
55 }
56}
57
58impl Header for ContentLength {
59 fn name() -> HeaderName {
60 CONTENT_LENGTH
61 }
62
63 fn parse<M: HttpMessage>(msg: &M) -> Result<Self, ParseError> {
64 let val = from_one_raw_str(msg.headers().get(Self::name()))?;
65
66 debug_assert_eq!(msg.headers().get_all(Self::name()).count(), 1);
68
69 Ok(val)
70 }
71}
72
73impl From<ContentLength> for usize {
74 fn from(ContentLength(len): ContentLength) -> Self {
75 len
76 }
77}
78
79impl From<usize> for ContentLength {
80 fn from(len: usize) -> Self {
81 ContentLength(len)
82 }
83}
84
85impl PartialEq<usize> for ContentLength {
86 fn eq(&self, other: &usize) -> bool {
87 self.0 == *other
88 }
89}
90
91impl PartialEq<ContentLength> for usize {
92 fn eq(&self, other: &ContentLength) -> bool {
93 *self == other.0
94 }
95}
96
97impl PartialOrd<usize> for ContentLength {
98 fn partial_cmp(&self, other: &usize) -> Option<std::cmp::Ordering> {
99 self.0.partial_cmp(other)
100 }
101}
102
103impl PartialOrd<ContentLength> for usize {
104 fn partial_cmp(&self, other: &ContentLength) -> Option<std::cmp::Ordering> {
105 self.partial_cmp(&other.0)
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::header::{assert_parse_eq, assert_parse_fail};
113
114 #[test]
115 fn missing_header() {
116 assert_parse_fail::<ContentLength, _, _>([""; 0]);
117 assert_parse_fail::<ContentLength, _, _>([""]);
118 }
119
120 #[test]
121 fn bad_header() {
122 assert_parse_fail::<ContentLength, _, _>(["-123"]);
123 assert_parse_fail::<ContentLength, _, _>(["123_456"]);
124 assert_parse_fail::<ContentLength, _, _>(["123.456"]);
125
126 assert_parse_fail::<ContentLength, _, _>(["18446744073709551616"]);
128 assert_parse_fail::<ContentLength, _, _>(["18446744073709551617"]);
129
130 assert_parse_fail::<ContentLength, _, _>(["0x123"]);
132
133 assert_parse_fail::<ContentLength, _, _>(["0, 123"]);
135 }
136
137 #[test]
138 #[should_panic]
139 fn bad_header_plus() {
140 assert_parse_fail::<ContentLength, _, _>(["+123"]);
142 }
143
144 #[test]
145 #[should_panic]
146 fn bad_multiple_value() {
147 assert_parse_fail::<ContentLength, _, _>(["0", "123"]);
148 }
149
150 #[test]
151 fn good_header() {
152 assert_parse_eq::<ContentLength, _, _>(["0"], ContentLength(0));
153 assert_parse_eq::<ContentLength, _, _>(["1"], ContentLength(1));
154 assert_parse_eq::<ContentLength, _, _>(["123"], ContentLength(123));
155
156 assert_parse_eq::<ContentLength, _, _>(["0123"], ContentLength(123));
158
159 assert_parse_eq::<ContentLength, _, _>([" 0"], ContentLength(0));
161 assert_parse_eq::<ContentLength, _, _>(["0 "], ContentLength(0));
162 assert_parse_eq::<ContentLength, _, _>([" 0 "], ContentLength(0));
163
164 assert_parse_eq::<ContentLength, _, _>(
166 ["18446744073709551615"],
167 ContentLength(18_446_744_073_709_551_615),
168 );
169 }
170
171 #[test]
172 fn equality() {
173 assert!(ContentLength(0) == ContentLength(0));
174 assert!(ContentLength(0) == 0);
175 assert!(0 != ContentLength(123));
176 }
177
178 #[test]
179 fn ordering() {
180 assert!(ContentLength(0) < ContentLength(123));
181 assert!(ContentLength(0) < 123);
182 assert!(0 < ContentLength(123));
183 }
184}