#[cfg(all(not(feature = "std"), feature = "alloc"))]
use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{fmt, str::FromStr};
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum ByteRange {
FromTo(u64, u64),
From(u64),
Suffix(u64),
}
impl ByteRange {
#[must_use]
pub fn is_valid(&self, length: u64) -> bool {
match self {
Self::FromTo(first, last) => first <= last && *first < length,
Self::From(first) => *first < length,
Self::Suffix(n) => *n > 0,
}
}
#[must_use]
pub fn resolve(&self, length: u64) -> Option<(u64, u64)> {
if length == 0 {
return None;
}
match self {
Self::FromTo(first, last) => {
if first > last || *first >= length {
None
} else {
Some((*first, (*last).min(length - 1)))
}
}
Self::From(first) => {
if *first >= length {
None
} else {
Some((*first, length - 1))
}
}
Self::Suffix(n) => {
if *n == 0 {
None
} else {
let first = length.saturating_sub(*n);
Some((first, length - 1))
}
}
}
}
}
impl fmt::Display for ByteRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::FromTo(first, last) => write!(f, "{first}-{last}"),
Self::From(first) => write!(f, "{first}-"),
Self::Suffix(n) => write!(f, "-{n}"),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum RangeHeader {
Bytes(Vec<ByteRange>),
Other(String),
}
impl fmt::Display for RangeHeader {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bytes(ranges) => {
f.write_str("bytes=")?;
for (i, r) in ranges.iter().enumerate() {
if i > 0 {
f.write_str(", ")?;
}
fmt::Display::fmt(r, f)?;
}
Ok(())
}
Self::Other(s) => f.write_str(s),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ParseRangeError {
Empty,
Malformed,
}
impl fmt::Display for ParseRangeError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => f.write_str("range header is empty"),
Self::Malformed => f.write_str("range header is malformed"),
}
}
}
#[cfg(feature = "std")]
impl std::error::Error for ParseRangeError {}
impl FromStr for ByteRange {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.is_empty() {
return Err(ParseRangeError::Empty);
}
if let Some(n) = s.strip_prefix('-') {
let n: u64 = n.parse().map_err(|_| ParseRangeError::Malformed)?;
return Ok(Self::Suffix(n));
}
if let Some(pos) = s.find('-') {
let first: u64 = s[..pos].parse().map_err(|_| ParseRangeError::Malformed)?;
let rest = &s[pos + 1..];
if rest.trim().is_empty() {
return Ok(Self::From(first));
}
let last: u64 = rest.parse().map_err(|_| ParseRangeError::Malformed)?;
return Ok(Self::FromTo(first, last));
}
Err(ParseRangeError::Malformed)
}
}
impl FromStr for RangeHeader {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
if s.is_empty() {
return Err(ParseRangeError::Empty);
}
if let Some(rest) = s.strip_prefix("bytes=") {
let ranges: Result<Vec<ByteRange>, _> = rest
.split(',')
.filter(|p| !p.trim().is_empty())
.map(|p| p.trim().parse::<ByteRange>())
.collect();
let ranges = ranges?;
if ranges.is_empty() {
return Err(ParseRangeError::Malformed);
}
return Ok(Self::Bytes(ranges));
}
Ok(Self::Other(s.to_string()))
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "utoipa", derive(utoipa::ToSchema))]
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
pub enum ContentRange {
Bytes {
first: u64,
last: u64,
complete_length: Option<u64>,
},
Unsatisfiable {
complete_length: u64,
},
}
impl ContentRange {
#[must_use]
pub fn bytes(first: u64, last: u64, complete_length: Option<u64>) -> Self {
Self::Bytes {
first,
last,
complete_length,
}
}
#[must_use]
pub fn bytes_unknown_length(first: u64, last: u64) -> Self {
Self::Bytes {
first,
last,
complete_length: None,
}
}
#[must_use]
pub fn unsatisfiable(complete_length: u64) -> Self {
Self::Unsatisfiable { complete_length }
}
}
impl fmt::Display for ContentRange {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Bytes {
first,
last,
complete_length,
} => {
write!(f, "bytes {first}-{last}/")?;
match complete_length {
Some(len) => write!(f, "{len}"),
None => f.write_str("*"),
}
}
Self::Unsatisfiable { complete_length } => {
write!(f, "bytes */{complete_length}")
}
}
}
}
impl FromStr for ContentRange {
type Err = ParseRangeError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let s = s.trim();
let rest = s.strip_prefix("bytes ").ok_or(ParseRangeError::Malformed)?;
if let Some(len_str) = rest.strip_prefix("*/") {
let complete_length: u64 = len_str.parse().map_err(|_| ParseRangeError::Malformed)?;
return Ok(Self::Unsatisfiable { complete_length });
}
let slash = rest.find('/').ok_or(ParseRangeError::Malformed)?;
let range_part = &rest[..slash];
let len_part = &rest[slash + 1..];
let dash = range_part.find('-').ok_or(ParseRangeError::Malformed)?;
let first: u64 = range_part[..dash]
.parse()
.map_err(|_| ParseRangeError::Malformed)?;
let last: u64 = range_part[dash + 1..]
.parse()
.map_err(|_| ParseRangeError::Malformed)?;
let complete_length = if len_part == "*" {
None
} else {
Some(
len_part
.parse::<u64>()
.map_err(|_| ParseRangeError::Malformed)?,
)
};
Ok(Self::Bytes {
first,
last,
complete_length,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn byte_range_from_to_display() {
assert_eq!(ByteRange::FromTo(0, 499).to_string(), "0-499");
}
#[test]
fn byte_range_from_display() {
assert_eq!(ByteRange::From(100).to_string(), "100-");
}
#[test]
fn byte_range_suffix_display() {
assert_eq!(ByteRange::Suffix(50).to_string(), "-50");
}
#[test]
fn byte_range_parse_from_to() {
let r: ByteRange = "0-499".parse().unwrap();
assert_eq!(r, ByteRange::FromTo(0, 499));
}
#[test]
fn byte_range_parse_from() {
let r: ByteRange = "100-".parse().unwrap();
assert_eq!(r, ByteRange::From(100));
}
#[test]
fn byte_range_parse_suffix() {
let r: ByteRange = "-50".parse().unwrap();
assert_eq!(r, ByteRange::Suffix(50));
}
#[test]
fn byte_range_roundtrip() {
let ranges = [
ByteRange::FromTo(0, 99),
ByteRange::From(500),
ByteRange::Suffix(200),
];
for r in &ranges {
let s = r.to_string();
let parsed: ByteRange = s.parse().unwrap();
assert_eq!(&parsed, r);
}
}
#[test]
fn byte_range_is_valid() {
assert!(ByteRange::FromTo(0, 499).is_valid(1000));
assert!(!ByteRange::FromTo(500, 200).is_valid(1000));
assert!(!ByteRange::FromTo(1000, 1999).is_valid(1000));
assert!(ByteRange::From(0).is_valid(1));
assert!(!ByteRange::From(1000).is_valid(1000));
assert!(ByteRange::Suffix(1).is_valid(1));
assert!(!ByteRange::Suffix(0).is_valid(1000));
}
#[test]
fn byte_range_resolve_from_to() {
assert_eq!(ByteRange::FromTo(0, 99).resolve(500), Some((0, 99)));
assert_eq!(ByteRange::FromTo(0, 999).resolve(500), Some((0, 499)));
assert_eq!(ByteRange::FromTo(500, 999).resolve(500), None);
assert_eq!(ByteRange::FromTo(0, 99).resolve(0), None);
}
#[test]
fn byte_range_resolve_from() {
assert_eq!(ByteRange::From(400).resolve(500), Some((400, 499)));
assert_eq!(ByteRange::From(500).resolve(500), None);
}
#[test]
fn byte_range_resolve_suffix() {
assert_eq!(ByteRange::Suffix(100).resolve(500), Some((400, 499)));
assert_eq!(ByteRange::Suffix(600).resolve(500), Some((0, 499)));
assert_eq!(ByteRange::Suffix(0).resolve(500), None);
}
#[test]
fn range_header_parse_single() {
let h: RangeHeader = "bytes=0-499".parse().unwrap();
assert_eq!(h, RangeHeader::Bytes(vec![ByteRange::FromTo(0, 499)]));
}
#[test]
fn range_header_parse_multi() {
let h: RangeHeader = "bytes=0-99, 200-299".parse().unwrap();
assert_eq!(
h,
RangeHeader::Bytes(vec![ByteRange::FromTo(0, 99), ByteRange::FromTo(200, 299)])
);
}
#[test]
fn range_header_parse_suffix() {
let h: RangeHeader = "bytes=-500".parse().unwrap();
assert_eq!(h, RangeHeader::Bytes(vec![ByteRange::Suffix(500)]));
}
#[test]
fn range_header_parse_open() {
let h: RangeHeader = "bytes=9500-".parse().unwrap();
assert_eq!(h, RangeHeader::Bytes(vec![ByteRange::From(9500)]));
}
#[test]
fn range_header_roundtrip() {
let h = RangeHeader::Bytes(vec![ByteRange::FromTo(0, 499), ByteRange::Suffix(50)]);
let s = h.to_string();
let parsed: RangeHeader = s.parse().unwrap();
assert_eq!(parsed, h);
}
#[test]
fn range_header_other() {
let h: RangeHeader = "items=0-9".parse().unwrap();
assert_eq!(h, RangeHeader::Other("items=0-9".to_string()));
}
#[test]
fn range_header_empty_errors() {
assert_eq!("".parse::<RangeHeader>(), Err(ParseRangeError::Empty));
}
#[test]
fn content_range_bytes_display() {
let cr = ContentRange::bytes(0, 999, Some(5000));
assert_eq!(cr.to_string(), "bytes 0-999/5000");
}
#[test]
fn content_range_bytes_unknown_length_display() {
let cr = ContentRange::bytes_unknown_length(200, 299);
assert_eq!(cr.to_string(), "bytes 200-299/*");
}
#[test]
fn content_range_unsatisfiable_display() {
let cr = ContentRange::unsatisfiable(5000);
assert_eq!(cr.to_string(), "bytes */5000");
}
#[test]
fn content_range_parse_known_length() {
let cr: ContentRange = "bytes 0-999/5000".parse().unwrap();
assert_eq!(cr, ContentRange::bytes(0, 999, Some(5000)));
}
#[test]
fn content_range_parse_unknown_length() {
let cr: ContentRange = "bytes 0-999/*".parse().unwrap();
assert_eq!(cr, ContentRange::bytes_unknown_length(0, 999));
}
#[test]
fn content_range_parse_unsatisfiable() {
let cr: ContentRange = "bytes */5000".parse().unwrap();
assert_eq!(cr, ContentRange::unsatisfiable(5000));
}
#[test]
fn content_range_roundtrip() {
let cases = [
ContentRange::bytes(0, 499, Some(1000)),
ContentRange::bytes_unknown_length(100, 199),
ContentRange::unsatisfiable(9999),
];
for cr in &cases {
let s = cr.to_string();
let parsed: ContentRange = s.parse().unwrap();
assert_eq!(&parsed, cr);
}
}
#[test]
fn byte_range_parse_empty_is_error() {
assert_eq!("".parse::<ByteRange>(), Err(ParseRangeError::Empty));
}
#[test]
fn parse_range_error_display() {
assert!(!ParseRangeError::Empty.to_string().is_empty());
assert!(!ParseRangeError::Malformed.to_string().is_empty());
}
#[test]
fn byte_range_parse_malformed_no_dash() {
assert_eq!("abc".parse::<ByteRange>(), Err(ParseRangeError::Malformed));
}
}