azure_storage_blobs/options/
ba512_range.rs

1use azure_core::{
2    error::{Error, ErrorKind, ResultExt},
3    headers::{self, Header},
4    prelude::Range,
5};
6use std::{fmt, str::FromStr};
7
8/// A 512 byte aligned byte range
9///
10/// [Read more here](https://docs.microsoft.com/en-us/rest/api/storageservices/specifying-the-range-header-for-blob-service-operations#format-2-bytesstartbyte-endbyte).
11#[derive(Debug, Copy, Clone, PartialEq, Eq)]
12pub struct BA512Range {
13    start: u64,
14    end: u64,
15}
16
17impl BA512Range {
18    pub fn start(&self) -> u64 {
19        self.start
20    }
21    pub fn end(&self) -> u64 {
22        self.end
23    }
24
25    pub fn new(start: u64, end: u64) -> azure_core::Result<Self> {
26        if start % 512 != 0 {
27            return Err(Error::with_message(ErrorKind::Other, || {
28                format!("start range not 512-byte aligned: {start}")
29            }));
30        }
31        if (end + 1) % 512 != 0 {
32            return Err(Error::with_message(ErrorKind::Other, || {
33                format!("end range not 512-byte aligned: {end}")
34            }));
35        }
36
37        Ok(Self { start, end })
38    }
39
40    #[inline]
41    pub fn size(&self) -> u64 {
42        self.end - self.start + 1
43    }
44}
45
46impl From<BA512Range> for Range {
47    fn from(range: BA512Range) -> Self {
48        (range.start()..range.end()).into()
49    }
50}
51
52impl TryFrom<Range> for BA512Range {
53    type Error = Error;
54
55    fn try_from(r: Range) -> azure_core::Result<Self> {
56        match r {
57            Range::Range(r) => BA512Range::new(r.start, r.end),
58            Range::RangeFrom(r) => Err(Error::with_message(ErrorKind::DataConversion, || {
59                format!("error converting RangeFrom<{:?}> into BA512Range", r)
60            })),
61        }
62    }
63}
64
65impl TryFrom<(u64, u64)> for BA512Range {
66    type Error = Error;
67
68    fn try_from((start, end): (u64, u64)) -> azure_core::Result<Self> {
69        BA512Range::new(start, end)
70    }
71}
72
73impl Header for BA512Range {
74    fn name(&self) -> headers::HeaderName {
75        headers::RANGE
76    }
77
78    fn value(&self) -> headers::HeaderValue {
79        self.to_string().into()
80    }
81}
82
83impl FromStr for BA512Range {
84    type Err = Error;
85    fn from_str(s: &str) -> azure_core::Result<BA512Range> {
86        let v = s.split('/').collect::<Vec<&str>>();
87        if v.len() != 2 {
88            return Err(Error::message(ErrorKind::Other, "split not found"));
89        }
90
91        let cp_start = v[0]
92            .parse::<u64>()
93            .with_context(ErrorKind::DataConversion, || {
94                format!("error parsing '{}' into u64", v[0])
95            })?;
96        let cp_end = v[1]
97            .parse::<u64>()
98            .with_context(ErrorKind::DataConversion, || {
99                format!("error parsing '{}' into u64", v[1])
100            })?;
101
102        BA512Range::new(cp_start, cp_end)
103    }
104}
105
106impl fmt::Display for BA512Range {
107    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108        write!(f, "bytes={}-{}", self.start, self.end)
109    }
110}
111
112impl<'a> From<&'a BA512Range> for Range {
113    fn from(ba: &'a BA512Range) -> Range {
114        (ba.start()..ba.end()).into()
115    }
116}
117
118#[cfg(test)]
119mod test {
120    use super::*;
121
122    #[test]
123    fn test_512range_parse() {
124        let range = "0/511".parse::<BA512Range>().unwrap();
125
126        assert_eq!(range.start, 0);
127        assert_eq!(range.end, 511);
128    }
129
130    #[test]
131    fn test_512range_parse_panic_1() {
132        let err = "abba/2000".parse::<BA512Range>().unwrap_err();
133        assert!(matches!(err.kind(), ErrorKind::DataConversion));
134    }
135
136    #[test]
137    fn test_512range_parse_panic_2() {
138        let err = "1000-2000".parse::<BA512Range>().unwrap_err();
139        assert!(matches!(err.kind(), ErrorKind::Other));
140    }
141
142    #[test]
143    fn test_512range_invalid_start_range() {
144        let err = "7/511".parse::<BA512Range>().unwrap_err();
145        assert!(matches!(err.kind(), ErrorKind::Other));
146    }
147
148    #[test]
149    fn test_512range_invalid_end_range() {
150        let err = "0/100".parse::<BA512Range>().unwrap_err();
151        assert!(matches!(err.kind(), ErrorKind::Other));
152    }
153
154    #[test]
155    fn test_512range_display() {
156        let range = BA512Range { start: 0, end: 511 };
157
158        let txt = format!("{range}");
159
160        assert_eq!(txt, "bytes=0-511");
161    }
162}