1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
2pub enum Range {
3 Full,
4 Slice { start: u64, end: u64 },
5 Unsatisfiable,
6}
7
8impl Range {
9 pub fn parse(header: Option<&str>, size: u64) -> Self {
10 let Some(header) = header else {
11 return Self::Full;
12 };
13
14 let Some(spec) = header.trim().strip_prefix("bytes=") else {
15 return Self::Full;
16 };
17
18 if spec.contains(',') {
19 return Self::Full;
20 }
21
22 let Some((first, last)) = spec.trim().split_once('-') else {
23 return Self::Full;
24 };
25
26 match (first.trim(), last.trim()) {
27 ("", suffix) => match suffix.parse::<u64>() {
28 Ok(0) => Self::Unsatisfiable,
29 Ok(length) if size > 0 => Self::Slice {
30 start: size.saturating_sub(length),
31 end: size - 1,
32 },
33 Ok(_) => Self::Unsatisfiable,
34 Err(_) => Self::Full,
35 },
36 (start, "") => match start.parse::<u64>() {
37 Ok(start) if start < size => Self::Slice {
38 start,
39 end: size - 1,
40 },
41 Ok(_) => Self::Unsatisfiable,
42 Err(_) => Self::Full,
43 },
44 (start, end) => match (start.parse::<u64>(), end.parse::<u64>()) {
45 (Ok(start), Ok(end)) if start < size && start <= end => Self::Slice {
46 start,
47 end: end.min(size - 1),
48 },
49 (Ok(_), Ok(_)) => Self::Unsatisfiable,
50 _ => Self::Full,
51 },
52 }
53 }
54
55 pub fn length(&self, size: u64) -> u64 {
56 match self {
57 Self::Slice { start, end } => end - start + 1,
58 _ => size,
59 }
60 }
61}
62
63#[cfg(test)]
64mod tests;