1 | use std::io; |
2 | use std::slice; |
3 | use std::str; |
4 | |
5 | use crate::other; |
6 | |
7 | /// An iterator over the pax extensions in an archive entry. |
8 | /// |
9 | /// This iterator yields structures which can themselves be parsed into |
10 | /// key/value pairs. |
11 | pub struct PaxExtensions<'entry> { |
12 | data: slice::Split<'entry, u8, fn(&u8) -> bool>, |
13 | } |
14 | |
15 | impl<'entry> PaxExtensions<'entry> { |
16 | /// Create new pax extensions iterator from the given entry data. |
17 | pub fn new(a: &'entry [u8]) -> Self { |
18 | fn is_newline(a: &u8) -> bool { |
19 | *a == b' \n' |
20 | } |
21 | PaxExtensions { |
22 | data: a.split(pred:is_newline), |
23 | } |
24 | } |
25 | } |
26 | |
27 | /// A key/value pair corresponding to a pax extension. |
28 | pub struct PaxExtension<'entry> { |
29 | key: &'entry [u8], |
30 | value: &'entry [u8], |
31 | } |
32 | |
33 | pub fn pax_extensions_size(a: &[u8]) -> Option<u64> { |
34 | for extension: Result, …> in PaxExtensions::new(a) { |
35 | let current_extension: PaxExtension<'_> = match extension { |
36 | Ok(ext: PaxExtension<'_>) => ext, |
37 | Err(_) => return None, |
38 | }; |
39 | if current_extension.key() != Ok("size" ) { |
40 | continue; |
41 | } |
42 | |
43 | let value: &str = match current_extension.value() { |
44 | Ok(value: &str) => value, |
45 | Err(_) => return None, |
46 | }; |
47 | let size: u64 = match value.parse::<u64>() { |
48 | Ok(size: u64) => size, |
49 | Err(_) => return None, |
50 | }; |
51 | return Some(size); |
52 | } |
53 | None |
54 | } |
55 | |
56 | impl<'entry> Iterator for PaxExtensions<'entry> { |
57 | type Item = io::Result<PaxExtension<'entry>>; |
58 | |
59 | fn next(&mut self) -> Option<io::Result<PaxExtension<'entry>>> { |
60 | let line = match self.data.next() { |
61 | Some(line) if line.is_empty() => return None, |
62 | Some(line) => line, |
63 | None => return None, |
64 | }; |
65 | |
66 | Some( |
67 | line.iter() |
68 | .position(|b| *b == b' ' ) |
69 | .and_then(|i| { |
70 | str::from_utf8(&line[..i]) |
71 | .ok() |
72 | .and_then(|len| len.parse::<usize>().ok().map(|j| (i + 1, j))) |
73 | }) |
74 | .and_then(|(kvstart, reported_len)| { |
75 | if line.len() + 1 == reported_len { |
76 | line[kvstart..] |
77 | .iter() |
78 | .position(|b| *b == b'=' ) |
79 | .map(|equals| (kvstart, equals)) |
80 | } else { |
81 | None |
82 | } |
83 | }) |
84 | .map(|(kvstart, equals)| PaxExtension { |
85 | key: &line[kvstart..kvstart + equals], |
86 | value: &line[kvstart + equals + 1..], |
87 | }) |
88 | .ok_or_else(|| other("malformed pax extension" )), |
89 | ) |
90 | } |
91 | } |
92 | |
93 | impl<'entry> PaxExtension<'entry> { |
94 | /// Returns the key for this key/value pair parsed as a string. |
95 | /// |
96 | /// May fail if the key isn't actually utf-8. |
97 | pub fn key(&self) -> Result<&'entry str, str::Utf8Error> { |
98 | str::from_utf8(self.key) |
99 | } |
100 | |
101 | /// Returns the underlying raw bytes for the key of this key/value pair. |
102 | pub fn key_bytes(&self) -> &'entry [u8] { |
103 | self.key |
104 | } |
105 | |
106 | /// Returns the value for this key/value pair parsed as a string. |
107 | /// |
108 | /// May fail if the value isn't actually utf-8. |
109 | pub fn value(&self) -> Result<&'entry str, str::Utf8Error> { |
110 | str::from_utf8(self.value) |
111 | } |
112 | |
113 | /// Returns the underlying raw bytes for this value of this key/value pair. |
114 | pub fn value_bytes(&self) -> &'entry [u8] { |
115 | self.value |
116 | } |
117 | } |
118 | |