1use std::{slice, str};
2
3use async_std::io;
4
5use crate::other;
6
7pub struct PaxExtensions<'entry> {
12 data: slice::Split<'entry, u8, fn(&u8) -> bool>,
13}
14
15pub struct PaxExtension<'entry> {
17 key: &'entry [u8],
18 value: &'entry [u8],
19}
20
21pub fn pax_extensions(a: &[u8]) -> PaxExtensions {
22 PaxExtensions {
23 data: a.split(|a| *a == b'\n'),
24 }
25}
26
27impl<'entry> Iterator for PaxExtensions<'entry> {
28 type Item = io::Result<PaxExtension<'entry>>;
29
30 fn next(&mut self) -> Option<io::Result<PaxExtension<'entry>>> {
31 let line = match self.data.next() {
32 Some(line) => {
33 if line.is_empty() {
34 return None;
35 } else {
36 line
37 }
38 }
39 None => return None,
40 };
41
42 Some(
43 line.iter()
44 .position(|b| *b == b' ')
45 .and_then(|i| {
46 str::from_utf8(&line[..i])
47 .ok()
48 .and_then(|len| len.parse::<usize>().ok().map(|j| (i + 1, j)))
49 })
50 .and_then(|(kvstart, reported_len)| {
51 if line.len() + 1 == reported_len {
52 line[kvstart..]
53 .iter()
54 .position(|b| *b == b'=')
55 .map(|equals| (kvstart, equals))
56 } else {
57 None
58 }
59 })
60 .map(|(kvstart, equals)| PaxExtension {
61 key: &line[kvstart..kvstart + equals],
62 value: &line[kvstart + equals + 1..],
63 })
64 .ok_or_else(|| other("malformed pax extension")),
65 )
66 }
67}
68
69impl<'entry> PaxExtension<'entry> {
70 pub fn key(&self) -> Result<&'entry str, str::Utf8Error> {
74 str::from_utf8(self.key)
75 }
76
77 pub fn key_bytes(&self) -> &'entry [u8] {
79 self.key
80 }
81
82 pub fn value(&self) -> Result<&'entry str, str::Utf8Error> {
86 str::from_utf8(self.value)
87 }
88
89 pub fn value_bytes(&self) -> &'entry [u8] {
91 self.value
92 }
93}