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