async_tar/
pax.rs

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
10/// An iterator over the pax extensions in an archive entry.
11///
12/// This iterator yields structures which can themselves be parsed into
13/// key/value pairs.
14pub struct PaxExtensions<'entry> {
15    data: slice::Split<'entry, u8, fn(&u8) -> bool>,
16}
17
18/// A key/value pair corresponding to a pax extension.
19pub 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    /// Returns the key for this key/value pair parsed as a string.
74    ///
75    /// May fail if the key isn't actually utf-8.
76    pub fn key(&self) -> Result<&'entry str, str::Utf8Error> {
77        str::from_utf8(self.key)
78    }
79
80    /// Returns the underlying raw bytes for the key of this key/value pair.
81    pub fn key_bytes(&self) -> &'entry [u8] {
82        self.key
83    }
84
85    /// Returns the value for this key/value pair parsed as a string.
86    ///
87    /// May fail if the value isn't actually utf-8.
88    pub fn value(&self) -> Result<&'entry str, str::Utf8Error> {
89        str::from_utf8(self.value)
90    }
91
92    /// Returns the underlying raw bytes for this value of this key/value pair.
93    pub fn value_bytes(&self) -> &'entry [u8] {
94        self.value
95    }
96}