1use std::fmt::Debug;
19use std::fmt::Display;
20use std::fmt::Formatter;
21use std::ops::Bound;
22use std::ops::Range;
23use std::ops::RangeBounds;
24use std::str::FromStr;
25
26use crate::*;
27
28#[derive(Default, Debug, Clone, Copy, Eq, PartialEq)]
43pub struct BytesRange(
44 u64,
46 Option<u64>,
48);
49
50impl BytesRange {
51 pub fn new(offset: u64, size: Option<u64>) -> Self {
62 BytesRange(offset, size)
63 }
64
65 pub fn offset(&self) -> u64 {
67 self.0
68 }
69
70 pub fn size(&self) -> Option<u64> {
72 self.1
73 }
74
75 pub fn advance(&mut self, n: u64) {
82 self.0 = self
83 .0
84 .checked_add(n)
85 .expect("BytesRange::advance overflow: offset + n exceeds u64::MAX");
86 self.1 = self.1.map(|size| {
87 size.checked_sub(n)
88 .expect("BytesRange::advance underflow: n exceeds range size")
89 });
90 }
91
92 pub fn is_full(&self) -> bool {
96 self.0 == 0 && self.1.is_none()
97 }
98
99 pub fn to_header(&self) -> String {
101 format!("bytes={self}")
102 }
103
104 pub fn to_range(&self) -> impl RangeBounds<u64> {
106 (
107 Bound::Included(self.0),
108 match self.1 {
109 Some(size) => Bound::Excluded(
110 self.0
111 .checked_add(size)
112 .expect("BytesRange::to_range overflow: offset + size exceeds u64::MAX"),
113 ),
114 None => Bound::Unbounded,
115 },
116 )
117 }
118
119 pub fn to_range_as_usize(self) -> impl RangeBounds<usize> {
121 let offset: usize = self
122 .0
123 .try_into()
124 .expect("BytesRange::to_range_as_usize: offset exceeds usize::MAX");
125 (
126 Bound::Included(offset),
127 match self.1 {
128 Some(size) => {
129 let end: usize = self
130 .0
131 .checked_add(size)
132 .expect(
133 "BytesRange::to_range_as_usize overflow: offset + size exceeds u64::MAX",
134 )
135 .try_into()
136 .expect(
137 "BytesRange::to_range_as_usize: offset + size exceeds usize::MAX",
138 );
139 Bound::Excluded(end)
140 }
141 None => Bound::Unbounded,
142 },
143 )
144 }
145
146 pub fn to_content_range(self, content_length: usize) -> Result<Range<usize>> {
148 if self.1 == Some(0) {
149 return Ok(0..0);
150 }
151
152 if self.0 >= content_length as u64 && self.1.is_none() {
153 return Ok(content_length..content_length);
154 }
155
156 let offset: usize = self.0.try_into().map_err(|_| {
157 Error::new(ErrorKind::RangeNotSatisfied, "range exceeds content length")
158 .with_operation("BytesRange::to_content_range")
159 .with_context("offset", self.0)
160 .with_context("content_length", content_length)
161 })?;
162
163 match self.1 {
164 Some(size) => {
165 let end = self
166 .0
167 .checked_add(size)
168 .ok_or_else(|| {
169 Error::new(ErrorKind::RangeNotSatisfied, "range exceeds content length")
170 .with_operation("BytesRange::to_content_range")
171 .with_context("offset", self.0)
172 .with_context("size", size)
173 .with_context("content_length", content_length)
174 })?
175 .try_into()
176 .map_err(|_| {
177 Error::new(ErrorKind::RangeNotSatisfied, "range exceeds content length")
178 .with_operation("BytesRange::to_content_range")
179 .with_context("offset", self.0)
180 .with_context("size", size)
181 .with_context("content_length", content_length)
182 })?;
183
184 if end > content_length {
185 return Err(Error::new(
186 ErrorKind::RangeNotSatisfied,
187 "range exceeds content length",
188 )
189 .with_operation("BytesRange::to_content_range")
190 .with_context("offset", self.0)
191 .with_context("size", size)
192 .with_context("content_length", content_length));
193 }
194
195 Ok(offset..end)
196 }
197 None => Ok(offset..content_length),
198 }
199 }
200}
201
202impl Display for BytesRange {
203 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
204 match self.1 {
205 None => write!(f, "{}-", self.0),
206 Some(0) => Err(std::fmt::Error),
208 Some(size) => write!(
209 f,
210 "{}-{}",
211 self.0,
212 self.0.checked_add(size - 1).ok_or(std::fmt::Error)?
213 ),
214 }
215 }
216}
217
218impl FromStr for BytesRange {
219 type Err = Error;
220
221 fn from_str(value: &str) -> Result<Self> {
222 let s = value.strip_prefix("bytes=").ok_or_else(|| {
223 Error::new(ErrorKind::Unexpected, "header range is invalid")
224 .with_operation("BytesRange::from_str")
225 .with_context("value", value)
226 })?;
227
228 if s.contains(',') {
229 return Err(Error::new(ErrorKind::Unexpected, "header range is invalid")
230 .with_operation("BytesRange::from_str")
231 .with_context("value", value));
232 }
233
234 let v = s.split('-').collect::<Vec<_>>();
235 if v.len() != 2 {
236 return Err(Error::new(ErrorKind::Unexpected, "header range is invalid")
237 .with_operation("BytesRange::from_str")
238 .with_context("value", value));
239 }
240
241 let parse_int_error = |e: std::num::ParseIntError| {
242 Error::new(ErrorKind::Unexpected, "header range is invalid")
243 .with_operation("BytesRange::from_str")
244 .with_context("value", value)
245 .set_source(e)
246 };
247
248 if v[1].is_empty() {
249 Ok(BytesRange::new(
251 v[0].parse().map_err(parse_int_error)?,
252 None,
253 ))
254 } else if v[0].is_empty() {
255 Err(Error::new(
257 ErrorKind::Unexpected,
258 "header range with tailing is not supported",
259 )
260 .with_operation("BytesRange::from_str")
261 .with_context("value", value))
262 } else {
263 let start: u64 = v[0].parse().map_err(parse_int_error)?;
265 let end: u64 = v[1].parse().map_err(parse_int_error)?;
266 if end < start {
267 return Err(Error::new(
268 ErrorKind::Unexpected,
269 "header range is invalid: end is less than start",
270 )
271 .with_operation("BytesRange::from_str")
272 .with_context("value", value));
273 }
274 Ok(BytesRange::new(start, Some(end - start + 1)))
275 }
276 }
277}
278
279impl<T> From<T> for BytesRange
280where
281 T: RangeBounds<u64>,
282{
283 fn from(range: T) -> Self {
284 let offset = match range.start_bound().cloned() {
285 Bound::Included(n) => n,
286 Bound::Excluded(n) => n.saturating_add(1),
287 Bound::Unbounded => 0,
288 };
289 let size = match range.end_bound().cloned() {
290 Bound::Included(n) => Some(n.saturating_add(1).saturating_sub(offset)),
291 Bound::Excluded(n) => Some(n.saturating_sub(offset)),
292 Bound::Unbounded => None,
293 };
294
295 BytesRange(offset, size)
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_bytes_range_display_zero_size() {
305 let range = BytesRange::new(0, Some(0));
307 assert!(std::fmt::write(&mut String::new(), format_args!("{}", range)).is_err());
308
309 let range = BytesRange::new(5, Some(0));
311 assert!(std::fmt::write(&mut String::new(), format_args!("{}", range)).is_err());
312 }
313
314 #[test]
315 fn test_bytes_range_to_string() {
316 let h = BytesRange::new(0, Some(1024));
317 assert_eq!(h.to_string(), "0-1023");
318
319 let h = BytesRange::new(1024, None);
320 assert_eq!(h.to_string(), "1024-");
321
322 let h = BytesRange::new(1024, Some(1024));
323 assert_eq!(h.to_string(), "1024-2047");
324 }
325
326 #[test]
327 fn test_bytes_range_to_header() {
328 let h = BytesRange::new(0, Some(1024));
329 assert_eq!(h.to_header(), "bytes=0-1023");
330
331 let h = BytesRange::new(1024, None);
332 assert_eq!(h.to_header(), "bytes=1024-");
333
334 let h = BytesRange::new(1024, Some(1024));
335 assert_eq!(h.to_header(), "bytes=1024-2047");
336 }
337
338 #[test]
339 fn test_bytes_range_from_range_bounds() {
340 assert_eq!(BytesRange::new(0, None), BytesRange::from(..));
341 assert_eq!(BytesRange::new(10, None), BytesRange::from(10..));
342 assert_eq!(BytesRange::new(0, Some(11)), BytesRange::from(..=10));
343 assert_eq!(BytesRange::new(0, Some(10)), BytesRange::from(..10));
344 assert_eq!(BytesRange::new(10, Some(10)), BytesRange::from(10..20));
345 assert_eq!(BytesRange::new(10, Some(11)), BytesRange::from(10..=20));
346 }
347
348 #[test]
349 fn test_bytes_range_from_str() -> Result<()> {
350 let cases = vec![
351 ("range-start", "bytes=123-", BytesRange::new(123, None)),
352 ("range", "bytes=123-124", BytesRange::new(123, Some(2))),
353 ("one byte", "bytes=0-0", BytesRange::new(0, Some(1))),
354 (
355 "lower case header",
356 "bytes=0-0",
357 BytesRange::new(0, Some(1)),
358 ),
359 ];
360
361 for (name, input, expected) in cases {
362 let actual = input.parse()?;
363
364 assert_eq!(expected, actual, "{name}")
365 }
366
367 Ok(())
368 }
369
370 #[test]
371 fn test_bytes_range_from_str_invalid_end_less_than_start() {
372 let cases = vec!["bytes=100-50", "bytes=10-9", "bytes=1-0"];
373
374 for input in cases {
375 let result: Result<BytesRange> = input.parse();
376 assert!(
377 result.is_err(),
378 "expected error for invalid range {input}, got {result:?}"
379 );
380 }
381 }
382
383 #[allow(clippy::reversed_empty_ranges)]
384 #[test]
385 fn test_bytes_range_from_range_bounds_underflow() {
386 assert_eq!(BytesRange::new(100, Some(0)), BytesRange::from(100..50));
389 assert_eq!(BytesRange::new(10, Some(0)), BytesRange::from(10..=5));
390 assert_eq!(BytesRange::new(5, Some(0)), BytesRange::from(5..0));
391 assert_eq!(BytesRange::new(5, Some(0)), BytesRange::from(5..=0));
392 }
393
394 #[test]
395 fn test_bytes_range_from_range_bounds_u64_max() {
396 assert_eq!(
398 BytesRange::new(0, Some(u64::MAX)),
399 BytesRange::from(..=u64::MAX)
400 );
401 assert_eq!(
402 BytesRange::new(0, Some(u64::MAX)),
403 BytesRange::from(..u64::MAX)
404 );
405 assert_eq!(
406 BytesRange::new(1, Some(u64::MAX.saturating_sub(1))),
407 BytesRange::from(1..=u64::MAX)
408 );
409 assert_eq!(
411 BytesRange::new(u64::MAX, None),
412 BytesRange::from((u64::MAX)..)
413 );
414 }
415
416 #[test]
417 fn test_bytes_range_display_overflow() {
418 let range = BytesRange::new(u64::MAX, Some(2));
420 assert!(std::fmt::write(&mut String::new(), format_args!("{}", range)).is_err());
421 }
422
423 #[test]
424 #[should_panic(expected = "BytesRange::to_range overflow")]
425 fn test_bytes_range_to_range_overflow() {
426 let range = BytesRange::new(u64::MAX, Some(1));
427 let _ = range.to_range();
428 }
429
430 #[test]
431 #[should_panic(expected = "BytesRange::to_range_as_usize")]
432 fn test_bytes_range_to_range_as_usize_overflow() {
433 let range = BytesRange::new(u64::MAX, Some(1));
434 let _ = range.to_range_as_usize();
435 }
436
437 #[test]
438 fn test_bytes_range_to_content_range() -> Result<()> {
439 assert_eq!(BytesRange::new(2, Some(4)).to_content_range(10)?, 2..6);
440 assert_eq!(BytesRange::new(2, None).to_content_range(10)?, 2..10);
441 assert_eq!(BytesRange::new(10, None).to_content_range(10)?, 10..10);
442 assert_eq!(BytesRange::new(20, None).to_content_range(10)?, 10..10);
443 assert_eq!(
444 BytesRange::new(u64::MAX, None).to_content_range(10)?,
445 10..10
446 );
447 assert_eq!(
448 BytesRange::new(u64::MAX, Some(0)).to_content_range(10)?,
449 0..0
450 );
451
452 let err = BytesRange::new(8, Some(4))
453 .to_content_range(10)
454 .unwrap_err();
455 assert_eq!(err.kind(), ErrorKind::RangeNotSatisfied);
456
457 let err = BytesRange::new(u64::MAX, Some(1))
458 .to_content_range(10)
459 .unwrap_err();
460 assert_eq!(err.kind(), ErrorKind::RangeNotSatisfied);
461
462 Ok(())
463 }
464
465 #[test]
466 #[should_panic(expected = "BytesRange::advance overflow")]
467 fn test_bytes_range_advance_offset_overflow() {
468 let mut range = BytesRange::new(u64::MAX, None);
469 range.advance(1);
470 }
471
472 #[test]
473 #[should_panic(expected = "BytesRange::advance underflow")]
474 fn test_bytes_range_advance_size_underflow() {
475 let mut range = BytesRange::new(0, Some(1));
476 range.advance(2);
477 }
478}