libpna/entry/
attr.rs

1use std::{io, mem, str};
2
3/// Entry extended attribute.
4#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
5pub struct ExtendedAttribute {
6    name: String,
7    value: Vec<u8>,
8}
9
10impl ExtendedAttribute {
11    /// Creates a new [`ExtendedAttribute`].
12    ///
13    /// # Examples
14    /// ```rust
15    /// use libpna::ExtendedAttribute;
16    ///
17    /// let xattr = ExtendedAttribute::new("name".into(), b"value".into());
18    /// ```
19    #[inline]
20    pub const fn new(name: String, value: Vec<u8>) -> Self {
21        Self { name, value }
22    }
23
24    /// Attribute name
25    ///
26    /// # Examples
27    /// ```rust
28    /// use libpna::ExtendedAttribute;
29    ///
30    /// let xattr = ExtendedAttribute::new("name".into(), b"value".into());
31    /// assert_eq!("name", xattr.name());
32    /// ```
33    #[inline]
34    pub fn name(&self) -> &str {
35        &self.name
36    }
37
38    /// Attribute value
39    ///
40    /// # Examples
41    /// ```rust
42    /// use libpna::ExtendedAttribute;
43    ///
44    /// let xattr = ExtendedAttribute::new("name".into(), b"value".into());
45    /// assert_eq!(b"value", xattr.value());
46    /// ```
47    #[inline]
48    pub fn value(&self) -> &[u8] {
49        &self.value
50    }
51
52    pub(crate) fn try_from_bytes(value: &[u8]) -> io::Result<Self> {
53        let (len, value) = value
54            .split_first_chunk::<{ mem::size_of::<u32>() }>()
55            .ok_or(io::ErrorKind::UnexpectedEof)?;
56        let len = u32::from_be_bytes(*len) as usize;
57        let (name, value) = value
58            .split_at_checked(len)
59            .ok_or(io::ErrorKind::UnexpectedEof)?;
60        let name = str::from_utf8(name).map_err(|_| io::ErrorKind::InvalidData)?;
61
62        let (len, value) = value
63            .split_first_chunk::<{ mem::size_of::<u32>() }>()
64            .ok_or(io::ErrorKind::UnexpectedEof)?;
65        let len = u32::from_be_bytes(*len) as usize;
66        let value = value.get(..len).ok_or(io::ErrorKind::UnexpectedEof)?;
67        let value = value.to_vec();
68        let name = name.to_owned();
69        Ok(Self { name, value })
70    }
71
72    pub(crate) fn to_bytes(&self) -> Vec<u8> {
73        let mut vec =
74            Vec::with_capacity(self.name.len() + self.value.len() + mem::size_of::<u32>() * 2);
75        vec.extend_from_slice(&(self.name.len() as u32).to_be_bytes());
76        vec.extend_from_slice(self.name.as_bytes());
77        vec.extend_from_slice(&(self.value.len() as u32).to_be_bytes());
78        vec.extend_from_slice(&self.value);
79        vec
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use super::*;
86    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
87    use wasm_bindgen_test::wasm_bindgen_test as test;
88
89    #[test]
90    fn xattr() {
91        let xattr = ExtendedAttribute::new("name".into(), "value".into());
92        assert_eq!(
93            xattr,
94            ExtendedAttribute::try_from_bytes(&xattr.to_bytes()).unwrap()
95        );
96    }
97}