1use std::{io, mem};
2
3#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
5pub struct ExtendedAttribute {
6 name: String,
7 value: Vec<u8>,
8}
9
10impl ExtendedAttribute {
11 #[inline]
20 pub const fn new(name: String, value: Vec<u8>) -> Self {
21 Self { name, value }
22 }
23
24 #[inline]
34 pub fn name(&self) -> &str {
35 &self.name
36 }
37
38 #[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}