1use std::{
3 fmt,
4 ops::{Range, RangeFrom, RangeFull, RangeInclusive, RangeTo, RangeToInclusive},
5};
6
7use crate::errors::CondowError;
8
9#[derive(Debug, Copy, Clone, PartialEq, Eq)]
25pub struct InclusiveRange(pub u64, pub u64);
26
27impl InclusiveRange {
28 pub fn start(&self) -> u64 {
38 self.0
39 }
40
41 pub fn end_incl(&self) -> u64 {
51 self.1
52 }
53
54 pub fn validate(&self) -> Result<(), CondowError> {
55 if self.end_incl() < self.start() {
56 Err(CondowError::new_invalid_range(format!(
57 "End must not be smaller than start: {}",
58 self
59 )))
60 } else {
61 Ok(())
62 }
63 }
64
65 #[allow(clippy::len_without_is_empty)]
75 #[inline]
76 pub fn len(&self) -> u64 {
77 if self.1 < self.0 {
78 return 0;
79 }
80
81 self.1 - self.0 + 1
82 }
83
84 pub fn to_std_range(self) -> RangeInclusive<u64> {
85 self.0..=self.1
86 }
87
88 #[cfg(test)]
89 pub fn to_std_range_usize(self) -> RangeInclusive<usize> {
90 self.0 as usize..=self.1 as usize
91 }
92
93 pub fn to_std_range_excl(self) -> Range<u64> {
94 self.0..self.1 + 1
95 }
96
97 #[inline]
98 pub fn advance(&mut self, by: u64) {
99 self.0 += by
100 }
101
102 pub fn http_bytes_range_value(&self) -> String {
112 format!("bytes={}-{}", self.0, self.1)
113 }
114}
115
116impl fmt::Display for InclusiveRange {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 write!(f, "[{}..{}]", self.0, self.1)
119 }
120}
121
122impl From<RangeInclusive<u64>> for InclusiveRange {
123 fn from(ri: RangeInclusive<u64>) -> Self {
124 Self(*ri.start(), *ri.end())
125 }
126}
127
128impl From<RangeToInclusive<u64>> for InclusiveRange {
129 fn from(ri: RangeToInclusive<u64>) -> Self {
130 Self(0, ri.end)
131 }
132}
133impl From<InclusiveRange> for RangeInclusive<u64> {
134 fn from(ir: InclusiveRange) -> Self {
135 ir.to_std_range()
136 }
137}
138
139impl From<InclusiveRange> for Range<u64> {
140 fn from(ir: InclusiveRange) -> Self {
141 ir.to_std_range_excl()
142 }
143}
144
145#[derive(Debug, Copy, Clone, PartialEq, Eq)]
147pub struct OffsetRange(pub u64, pub u64);
148
149impl OffsetRange {
150 pub fn new(offset: u64, len: u64) -> Self {
151 Self(offset, len)
152 }
153
154 pub fn start(&self) -> u64 {
164 self.0
165 }
166
167 pub fn end_excl(&self) -> u64 {
177 self.0 + self.1
178 }
179
180 #[allow(clippy::len_without_is_empty)]
181 pub fn len(&self) -> u64 {
191 self.1
192 }
193}
194
195impl fmt::Display for OffsetRange {
196 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
197 write!(f, "{}({})", self.0, self.1)
198 }
199}
200
201#[derive(Debug, Clone, Copy, PartialEq)]
217pub enum ClosedRange {
218 FromTo(u64, u64),
220 FromToInclusive(u64, u64),
222 To(u64),
224 ToInclusive(u64),
226}
227
228impl ClosedRange {
229 pub fn validate(&self) -> Result<(), CondowError> {
267 match self {
268 Self::FromTo(a, b) => {
269 if b < a {
270 Err(CondowError::new_invalid_range(format!(
271 "FromTo: 'to'({b}) must not be lesser than 'from'({a})"
272 )))
273 } else {
274 Ok(())
275 }
276 }
277 Self::FromToInclusive(a, b) => {
278 if b < a {
279 Err(CondowError::new_invalid_range(format!(
280 "FromToInclusive: 'to'({b}) must not be lesser than 'from'({a})"
281 )))
282 } else {
283 Ok(())
284 }
285 }
286 _ => Ok(()),
287 }
288 }
289
290 pub fn is_empty(&self) -> bool {
291 self.len() == 0 }
293
294 pub fn len(&self) -> u64 {
295 match self {
296 Self::FromTo(a, b) => b - a,
297 Self::FromToInclusive(a, b) => b - a + 1,
298 Self::To(last_excl) => *last_excl,
299 Self::ToInclusive(last_incl) => last_incl + 1,
300 }
301 }
302
303 pub fn sanitized(self) -> Option<Self> {
304 match self {
305 Self::FromTo(a, b) => {
306 if b <= a {
307 return None;
308 }
309 }
310 Self::FromToInclusive(a, b) => {
311 if b < a {
312 return None;
313 }
314 }
315 Self::To(0) => return None,
316 Self::To(_) => {}
317 Self::ToInclusive(_) => {}
318 }
319
320 Some(self)
321 }
322
323 pub fn incl_range(self) -> Option<InclusiveRange> {
324 let inclusive = match self {
325 Self::FromTo(a, b) => {
326 if a == b {
327 return None;
328 }
329 InclusiveRange(a, b - 1)
330 }
331 Self::FromToInclusive(a, b) => InclusiveRange(a, b),
332 Self::To(b) => {
333 if b == 0 {
334 return None;
335 }
336 InclusiveRange(0, b - 1)
337 }
338 Self::ToInclusive(b) => InclusiveRange(0, b),
339 };
340
341 Some(inclusive)
342 }
343}
344
345impl fmt::Display for ClosedRange {
346 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
347 match self {
348 ClosedRange::To(to) => write!(f, "[0..{to}["),
349 ClosedRange::ToInclusive(to) => write!(f, "[0..{to}]"),
350 ClosedRange::FromTo(from, to) => write!(f, "[{from}..{to}["),
351 ClosedRange::FromToInclusive(from, to) => write!(f, "[{from}..{to}]"),
352 }
353 }
354}
355
356impl From<Range<u64>> for ClosedRange {
357 fn from(r: Range<u64>) -> Self {
358 ClosedRange::FromTo(r.start, r.end)
359 }
360}
361
362impl From<RangeInclusive<u64>> for ClosedRange {
363 fn from(r: RangeInclusive<u64>) -> Self {
364 ClosedRange::FromToInclusive(*r.start(), *r.end())
365 }
366}
367
368impl From<RangeTo<u64>> for ClosedRange {
369 fn from(r: RangeTo<u64>) -> Self {
370 ClosedRange::To(r.end)
371 }
372}
373
374impl From<RangeToInclusive<u64>> for ClosedRange {
375 fn from(r: RangeToInclusive<u64>) -> Self {
376 ClosedRange::ToInclusive(r.end)
377 }
378}
379
380impl From<InclusiveRange> for ClosedRange {
381 fn from(r: InclusiveRange) -> Self {
382 ClosedRange::FromToInclusive(r.0, r.1)
383 }
384}
385
386#[derive(Debug, Clone, Copy, PartialEq)]
392pub enum OpenRange {
393 From(u64),
395 Full,
397}
398
399impl OpenRange {
400 pub fn incl_range_from_size(self, size: u64) -> Result<Option<InclusiveRange>, CondowError> {
401 if size == 0 {
402 return Ok(None);
403 }
404
405 let max_inclusive = size - 1;
406 let inclusive = match self {
407 Self::From(a) => InclusiveRange(a, max_inclusive),
408 Self::Full => InclusiveRange(0, max_inclusive),
409 };
410
411 inclusive.validate()?;
412
413 Ok(Some(inclusive))
414 }
415}
416
417impl fmt::Display for OpenRange {
418 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
419 match self {
420 OpenRange::From(from) => write!(f, "[{}..]", from),
421 OpenRange::Full => write!(f, "[0..]"),
422 }
423 }
424}
425
426#[derive(Debug, Clone, Copy, PartialEq)]
474pub enum DownloadRange {
475 Open(OpenRange),
476 Closed(ClosedRange),
477}
478
479impl DownloadRange {
480 pub fn validate(&self) -> Result<(), CondowError> {
481 match self {
482 DownloadRange::Open(_) => Ok(()),
483 DownloadRange::Closed(r) => r.validate(),
484 }
485 }
486
487 pub fn sanitized(self) -> Option<Self> {
488 match self {
489 DownloadRange::Open(_) => Some(self),
490 DownloadRange::Closed(r) => r.sanitized().map(DownloadRange::Closed),
491 }
492 }
493
494 pub fn incl_range_from_size(self, size: u64) -> Result<Option<InclusiveRange>, CondowError> {
495 match self {
496 DownloadRange::Open(r) => r.incl_range_from_size(size),
497 DownloadRange::Closed(r) => {
498 let inclusive = r.incl_range();
499 if let Some(inclusive) = inclusive {
500 inclusive.validate()?
501 }
502 Ok(inclusive)
503 }
504 }
505 }
506
507 pub fn len(&self) -> Option<u64> {
509 match self {
510 DownloadRange::Open(_) => None,
511 DownloadRange::Closed(r) => Some(r.len()),
512 }
513 }
514
515 pub fn is_empty(&self) -> Option<bool> {
516 match self {
517 DownloadRange::Open(_) => None,
518 DownloadRange::Closed(r) => Some(r.is_empty()),
519 }
520 }
521}
522
523impl fmt::Display for DownloadRange {
524 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
525 match self {
526 DownloadRange::Open(open) => open.fmt(f),
527 DownloadRange::Closed(closed) => closed.fmt(f),
528 }
529 }
530}
531
532impl From<RangeFull> for DownloadRange {
533 fn from(_: RangeFull) -> Self {
534 Self::Open(OpenRange::Full)
535 }
536}
537
538impl From<Range<u64>> for DownloadRange {
539 fn from(r: Range<u64>) -> Self {
540 Self::Closed(r.into())
541 }
542}
543
544impl From<RangeInclusive<u64>> for DownloadRange {
545 fn from(r: RangeInclusive<u64>) -> Self {
546 Self::Closed(r.into())
547 }
548}
549
550impl From<RangeFrom<u64>> for DownloadRange {
551 fn from(r: RangeFrom<u64>) -> Self {
552 Self::Open(OpenRange::From(r.start))
553 }
554}
555
556impl From<RangeTo<u64>> for DownloadRange {
557 fn from(r: RangeTo<u64>) -> Self {
558 Self::Closed(r.into())
559 }
560}
561
562impl From<RangeToInclusive<u64>> for DownloadRange {
563 fn from(r: RangeToInclusive<u64>) -> Self {
564 Self::Closed(r.into())
565 }
566}
567
568impl From<InclusiveRange> for DownloadRange {
569 fn from(r: InclusiveRange) -> Self {
570 Self::Closed(r.into())
571 }
572}
573
574impl From<OffsetRange> for DownloadRange {
575 fn from(r: OffsetRange) -> Self {
576 Self::Closed(ClosedRange::FromTo(r.start(), r.end_excl()))
577 }
578}
579
580impl From<ClosedRange> for DownloadRange {
581 fn from(r: ClosedRange) -> Self {
582 Self::Closed(r)
583 }
584}
585
586#[test]
587fn range_full() {
588 let result: DownloadRange = (..).into();
589 assert_eq!(result, DownloadRange::Open(OpenRange::Full));
590}
591
592#[test]
593fn range() {
594 let result: DownloadRange = (3..10).into();
595 assert_eq!(result, DownloadRange::Closed(ClosedRange::FromTo(3, 10)));
596}
597
598#[test]
599fn range_inclusive() {
600 let result: DownloadRange = (3..=10).into();
601 assert_eq!(
602 result,
603 DownloadRange::Closed(ClosedRange::FromToInclusive(3, 10))
604 );
605}
606
607#[test]
608fn range_from() {
609 let result: DownloadRange = (3..).into();
610 assert_eq!(result, DownloadRange::Open(OpenRange::From(3)));
611}
612
613#[test]
614fn range_to() {
615 let result: DownloadRange = (..10).into();
616 assert_eq!(result, DownloadRange::Closed(ClosedRange::To(10)));
617}
618
619#[test]
620fn range_to_inclusive() {
621 let result: DownloadRange = (..=10).into();
622 assert_eq!(result, DownloadRange::Closed(ClosedRange::ToInclusive(10)));
623}