use std::{
fmt::Debug,
ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
};
use reqwest::header::HeaderValue;
#[allow(clippy::module_name_repetitions)]
pub trait ByteRange: Debug {
fn start(&self) -> u64;
fn end(&self) -> Option<u64> {
self.len().map(|len| len + self.start() - 1)
}
fn len(&self) -> Option<u64>;
fn is_empty(&self) -> bool {
self.len().map_or(false, |len| len == 0)
}
fn to_http_range(&self) -> String {
format!(
"{}-{}",
self.start(),
self.end().map(|n| n.to_string()).unwrap_or_default(),
)
}
fn to_http(&self) -> HeaderValue {
let s = format!("bytes={}", self.to_http_range());
HeaderValue::from_str(&s).unwrap()
}
}
impl ByteRange for OpenByteRange {
fn start(&self) -> u64 {
self.start
}
fn len(&self) -> Option<u64> {
None
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::module_name_repetitions)]
pub struct OpenByteRange {
start: u64,
}
impl OpenByteRange {
#[must_use]
pub fn new(start: u64) -> Self {
Self { start }
}
#[must_use]
pub fn full() -> Self {
Self::new(0)
}
}
impl From<RangeFrom<u64>> for OpenByteRange {
fn from(r: RangeFrom<u64>) -> Self {
Self { start: r.start }
}
}
impl From<RangeFull> for OpenByteRange {
fn from(_: RangeFull) -> Self {
Self { start: 0 }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
#[allow(clippy::module_name_repetitions)]
pub struct ClosedByteRange {
start: u64,
len: u64,
}
#[derive(Debug, thiserror::Error)]
pub enum InvalidRangeError {
#[error("range is backwards")]
Backwards,
}
impl ClosedByteRange {
#[must_use]
pub fn new(start: u64, len: u64) -> Self {
Self { start, len }
}
pub fn try_from_bounds(first: u64, last: u64) -> Result<Self, InvalidRangeError> {
if first > last {
Err(InvalidRangeError::Backwards)
} else {
Ok(Self::new(first, last - first + 1))
}
}
#[must_use]
pub fn new_to_including(end: u64) -> Self {
Self::new(0, end + 1)
}
#[must_use]
pub fn len(&self) -> u64 {
self.len
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.len == 0
}
#[must_use]
pub fn start(&self) -> u64 {
self.start
}
#[must_use]
pub fn end(&self) -> u64 {
self.start + self.len - 1
}
}
impl ByteRange for ClosedByteRange {
fn start(&self) -> u64 {
self.start
}
fn len(&self) -> Option<u64> {
Some(self.len)
}
}
impl TryFrom<Range<u64>> for ClosedByteRange {
type Error = InvalidRangeError;
fn try_from(r: Range<u64>) -> Result<Self, Self::Error> {
Self::try_from_bounds(r.start, r.end - 1)
}
}
impl TryFrom<RangeInclusive<u64>> for ClosedByteRange {
type Error = InvalidRangeError;
fn try_from(r: RangeInclusive<u64>) -> Result<Self, Self::Error> {
Self::try_from_bounds(*r.start(), *r.end())
}
}
impl From<RangeTo<u64>> for ClosedByteRange {
fn from(r: RangeTo<u64>) -> Self {
Self::new_to_including(r.end - 1)
}
}
impl From<RangeToInclusive<u64>> for ClosedByteRange {
fn from(r: RangeToInclusive<u64>) -> Self {
Self::new_to_including(r.end)
}
}