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) if line.is_empty() => return None,
33 Some(line) => line,
34 None => return None,
35 };
36
37 Some(
38 line.iter()
39 .position(|b| *b == b' ')
40 .and_then(|i| {
41 str::from_utf8(&line[..i])
42 .ok()
43 .and_then(|len| len.parse::<usize>().ok().map(|j| (i + 1, j)))
44 })
45 .and_then(|(kvstart, reported_len)| {
46 if line.len() + 1 == reported_len {
47 line[kvstart..]
48 .iter()
49 .position(|b| *b == b'=')
50 .map(|equals| (kvstart, equals))
51 } else {
52 None
53 }
54 })
55 .map(|(kvstart, equals)| PaxExtension {
56 key: &line[kvstart..kvstart + equals],
57 value: &line[kvstart + equals + 1..],
58 })
59 .ok_or_else(|| other("malformed pax extension")),
60 )
61 }
62}
63
64impl<'entry> PaxExtension<'entry> {
65 pub fn key(&self) -> Result<&'entry str, str::Utf8Error> {
69 str::from_utf8(self.key)
70 }
71
72 pub fn key_bytes(&self) -> &'entry [u8] {
74 self.key
75 }
76
77 pub fn value(&self) -> Result<&'entry str, str::Utf8Error> {
81 str::from_utf8(self.value)
82 }
83
84 pub fn value_bytes(&self) -> &'entry [u8] {
86 self.value
87 }
88}