hyper_sync/header/common/
content_range.rs1use std::fmt::{self, Display};
2use std::str::FromStr;
3
4header! {
5 (ContentRange, "Content-Range") => [ContentRangeSpec]
8
9 test_content_range {
10 test_header!(test_bytes,
11 vec![b"bytes 0-499/500"],
12 Some(ContentRange(ContentRangeSpec::Bytes {
13 range: Some((0, 499)),
14 instance_length: Some(500)
15 })));
16
17 test_header!(test_bytes_unknown_len,
18 vec![b"bytes 0-499/*"],
19 Some(ContentRange(ContentRangeSpec::Bytes {
20 range: Some((0, 499)),
21 instance_length: None
22 })));
23
24 test_header!(test_bytes_unknown_range,
25 vec![b"bytes */500"],
26 Some(ContentRange(ContentRangeSpec::Bytes {
27 range: None,
28 instance_length: Some(500)
29 })));
30
31 test_header!(test_unregistered,
32 vec![b"seconds 1-2"],
33 Some(ContentRange(ContentRangeSpec::Unregistered {
34 unit: "seconds".to_owned(),
35 resp: "1-2".to_owned()
36 })));
37
38 test_header!(test_no_len,
39 vec![b"bytes 0-499"],
40 None::<ContentRange>);
41
42 test_header!(test_only_unit,
43 vec![b"bytes"],
44 None::<ContentRange>);
45
46 test_header!(test_end_less_than_start,
47 vec![b"bytes 499-0/500"],
48 None::<ContentRange>);
49
50 test_header!(test_blank,
51 vec![b""],
52 None::<ContentRange>);
53
54 test_header!(test_bytes_many_spaces,
55 vec![b"bytes 1-2/500 3"],
56 None::<ContentRange>);
57
58 test_header!(test_bytes_many_slashes,
59 vec![b"bytes 1-2/500/600"],
60 None::<ContentRange>);
61
62 test_header!(test_bytes_many_dashes,
63 vec![b"bytes 1-2-3/500"],
64 None::<ContentRange>);
65
66 }
67}
68
69
70#[derive(PartialEq, Clone, Debug)]
91pub enum ContentRangeSpec {
92 Bytes {
94 range: Option<(u64, u64)>,
97
98 instance_length: Option<u64>
100 },
101
102 Unregistered {
104 unit: String,
106
107 resp: String
109 }
110}
111
112fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> {
113 let mut iter = s.splitn(2, separator);
114 match (iter.next(), iter.next()) {
115 (Some(a), Some(b)) => Some((a, b)),
116 _ => None
117 }
118}
119
120impl FromStr for ContentRangeSpec {
121 type Err = ::Error;
122
123 fn from_str(s: &str) -> ::Result<Self> {
124 let res = match split_in_two(s, ' ') {
125 Some(("bytes", resp)) => {
126 let (range, instance_length) = try!(split_in_two(resp, '/').ok_or(::Error::Header));
127
128 let instance_length = if instance_length == "*" {
129 None
130 } else {
131 Some(try!(instance_length.parse().map_err(|_| ::Error::Header)))
132 };
133
134 let range = if range == "*" {
135 None
136 } else {
137 let (first_byte, last_byte) = try!(split_in_two(range, '-').ok_or(::Error::Header));
138 let first_byte = try!(first_byte.parse().map_err(|_| ::Error::Header));
139 let last_byte = try!(last_byte.parse().map_err(|_| ::Error::Header));
140 if last_byte < first_byte {
141 return Err(::Error::Header);
142 }
143 Some((first_byte, last_byte))
144 };
145
146 ContentRangeSpec::Bytes {
147 range: range,
148 instance_length: instance_length
149 }
150 }
151 Some((unit, resp)) => {
152 ContentRangeSpec::Unregistered {
153 unit: unit.to_owned(),
154 resp: resp.to_owned()
155 }
156 }
157 _ => return Err(::Error::Header)
158 };
159 Ok(res)
160 }
161}
162
163impl Display for ContentRangeSpec {
164 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
165 match *self {
166 ContentRangeSpec::Bytes { range, instance_length } => {
167 try!(f.write_str("bytes "));
168 match range {
169 Some((first_byte, last_byte)) => {
170 try!(write!(f, "{}-{}", first_byte, last_byte));
171 },
172 None => {
173 try!(f.write_str("*"));
174 }
175 };
176 try!(f.write_str("/"));
177 if let Some(v) = instance_length {
178 write!(f, "{}", v)
179 } else {
180 f.write_str("*")
181 }
182 }
183 ContentRangeSpec::Unregistered { ref unit, ref resp } => {
184 try!(f.write_str(&unit));
185 try!(f.write_str(" "));
186 f.write_str(resp)
187 }
188 }
189 }
190}