libpna/entry/
attr.rs

1use std::{io, mem};
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    /// Create new [ExtendedAttribute].
12    ///
13    /// # Example
14    /// ```
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    /// # Example
27    /// ```
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    /// # Example
41    /// ```
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 = String::from_utf8(name.to_vec()).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        Ok(Self { name, value })
69    }
70
71    pub(crate) fn to_bytes(&self) -> Vec<u8> {
72        let mut vec =
73            Vec::with_capacity(self.name.len() + self.value.len() + mem::size_of::<u32>() * 2);
74        vec.extend_from_slice(&(self.name.len() as u32).to_be_bytes());
75        vec.extend_from_slice(self.name.as_bytes());
76        vec.extend_from_slice(&(self.value.len() as u32).to_be_bytes());
77        vec.extend_from_slice(&self.value);
78        vec
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85    #[cfg(all(target_family = "wasm", target_os = "unknown"))]
86    use wasm_bindgen_test::wasm_bindgen_test as test;
87
88    #[test]
89    fn xattr() {
90        let xattr = ExtendedAttribute::new("name".into(), "value".into());
91        assert_eq!(
92            xattr,
93            ExtendedAttribute::try_from_bytes(&xattr.to_bytes()).unwrap()
94        );
95    }
96}