range_header/
byte_range.rs

1//! reference: <https://tools.ietf.org/html/rfc7233>
2
3use pest::Parser;
4use pest_derive::Parser;
5
6#[derive(Parser)]
7#[grammar = "./byte_range.pest"]
8struct ByteRangeParser;
9
10#[derive(Debug, Eq, PartialEq, Clone, Hash)]
11pub enum ByteRange {
12    FromTo(u64),
13    FromToAll(u64, u64),
14    Last(u64),
15}
16
17impl ByteRange {
18    /// Parses Range HTTP header string as per RFC 2733,but `bytes` only.
19    /// With invalid input, return empty vector
20    ///
21    /// # Examples
22    ///
23    /// ```rust
24    /// use range_header::ByteRange;
25    /// assert_eq!(
26    ///     ByteRange::parse("bytes=10-100"),
27    ///     vec![ByteRange::FromToAll(10, 100)]
28    /// );
29    ///
30    /// assert_eq!(
31    ///     ByteRange::parse("bytes=10-"),
32    ///     vec![ByteRange::FromTo(10)]
33    /// );
34    ///
35    ///assert_eq!(
36    ///     ByteRange::parse("bytes=-100"),
37    ///     vec![ByteRange::Last(100)]
38    /// );
39    ///
40    /// assert_eq!(
41    ///     ByteRange::parse("invalid input"),
42    ///     vec![]
43    /// );
44    /// ```
45    pub fn parse(header: &str) -> Vec<Self> {
46        let byte_range_spec_iter = match ByteRangeParser::parse(Rule::byte_ranges_specifier, header)
47        {
48            Err(_) => {
49                return vec![];
50            }
51            Ok(x) => x.peek().unwrap().into_inner(),
52        };
53
54        let mut result = Vec::new();
55        for spec in byte_range_spec_iter {
56            match spec.as_rule() {
57                Rule::from_to => {
58                    // eg. '200-'
59                    let offset: u64 = spec.into_inner().peek().unwrap().as_str().parse().unwrap();
60                    result.push(ByteRange::FromTo(offset));
61                }
62                Rule::from_to_all => {
63                    // eg, '200-300'
64                    let mut inner_pairs = spec.into_inner();
65                    let begin: u64 = inner_pairs.next().unwrap().as_str().parse().unwrap();
66                    let end: u64 = inner_pairs.next().unwrap().as_str().parse().unwrap();
67
68                    if begin > end {
69                        continue;
70                    }
71                    result.push(ByteRange::FromToAll(begin, end));
72                }
73                Rule::last => {
74                    // eg. '-200'
75                    let length: u64 = spec.into_inner().peek().unwrap().as_str().parse().unwrap();
76                    result.push(ByteRange::Last(length));
77                }
78                Rule::EOI => {}
79                _ => unreachable!(),
80            }
81        }
82        result
83    }
84}