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