Skip to main content

range_requests/headers/
mod.rs

1use std::{
2    fmt::{self, Display},
3    num::ParseIntError,
4    ops::RangeInclusive,
5};
6
7pub mod content_range;
8pub mod if_range;
9pub mod range;
10#[cfg(test)]
11mod tests;
12
13const UNIT: &str = "bytes";
14
15/// The Errors that may occur during [`HttpContentRange`] and [`HttpRange`] parsing.
16///
17/// [`HttpRange`]: crate::headers::range::HttpRange
18/// [`HttpContentRange`]: crate::headers::content_range::HttpContentRange
19#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
20pub enum ParseHttpRangeOrContentRangeError {
21    #[error("The header value is malformed")]
22    Malformed,
23    #[error("Contains nonvisible ASCII")]
24    ContainsNonVisibleASCII,
25    #[error("Empty header value")]
26    Empty,
27    #[error("Invalid unit")]
28    InvalidUnit,
29    #[error("Invalid range value")]
30    MalformedRange,
31    #[error(transparent)]
32    UnorderedRange(#[from] InvalidOrderedRange),
33    #[error("Invalid range piece")]
34    InvalidRangePiece(#[source] InvalidHttpU64),
35    #[error("Invalid size value")]
36    InvalidSize(#[source] InvalidHttpU64),
37}
38
39#[cfg(feature = "axum")]
40impl axum_core::response::IntoResponse for ParseHttpRangeOrContentRangeError {
41    fn into_response(self) -> axum_core::response::Response {
42        http::StatusCode::BAD_REQUEST.into_response()
43    }
44}
45
46/// An error that may occur when parsing header values that are u64.
47#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
48pub enum InvalidHttpU64 {
49    #[error("{0} has a sign as prefix, so it can't be parsed as an unprefixed int")]
50    HasSignPrefix(String),
51    #[error(transparent)]
52    InvalidInt(#[from] ParseIntError),
53}
54
55/// An error that may occur when creating an [`OrderedRange`] with `start` > `end`.
56#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
57#[error("The provided `start`: {start} is greater than `end`: {end}")]
58pub struct InvalidOrderedRange {
59    start: u64,
60    end: u64,
61}
62
63/// An ordered range.
64#[derive(Debug, Clone, Copy, PartialEq, Eq)]
65pub struct OrderedRange {
66    start: u64,
67    end: u64,
68}
69
70impl OrderedRange {
71    pub fn new(range: RangeInclusive<u64>) -> Result<Self, InvalidOrderedRange> {
72        let start = *range.start();
73        let end = *range.end();
74
75        if start > end {
76            return Err(InvalidOrderedRange { start, end });
77        }
78
79        Ok(Self { start, end })
80    }
81
82    /// Returns the inclusive starting point of the range.
83    pub fn start(&self) -> u64 {
84        self.start
85    }
86
87    /// Returns the inclusive ending point of the range.
88    pub fn end(&self) -> u64 {
89        self.end
90    }
91}
92
93impl Display for OrderedRange {
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        write!(f, "{}-{}", self.start(), self.end())
96    }
97}
98
99pub(crate) fn u64_unprefixed_parse(s: &str) -> Result<u64, InvalidHttpU64> {
100    if s.starts_with("+") {
101        Err(InvalidHttpU64::HasSignPrefix(s.to_owned()))
102    } else {
103        Ok(s.parse::<u64>()?)
104    }
105}