box_format/file/
meta.rs

1use super::AttrMap;
2use crate::file::Inode;
3use crate::path::BoxPath;
4use crate::record::DirectoryRecord;
5use crate::Record;
6use std::{
7    borrow::Cow,
8    collections::{BTreeMap, VecDeque},
9    fmt::Display,
10};
11
12#[derive(Debug, Default)]
13pub struct BoxMetadata {
14    /// Root "directory" keyed by inodes
15    pub(crate) root: Vec<Inode>,
16
17    /// Keyed by inode index (offset by -1). This means if an `Inode` has the value 1, its index in this vector is 0.
18    /// This is to provide compatibility with platforms such as Linux, and allow for error checking a box file.
19    pub(crate) inodes: Vec<Record>,
20
21    /// The index of the attribute key is its interned identifier throughout this file.
22    pub(crate) attr_keys: Vec<String>,
23
24    /// The global attributes that apply to this entire box file.
25    pub(crate) attrs: AttrMap,
26    // /// The index of paths to files.
27    // pub(crate) index: Option<pathtrie::fst::Fst<u64>>,
28}
29
30#[derive(Debug)]
31pub struct Records<'a> {
32    meta: &'a BoxMetadata,
33    inodes: &'a [Inode],
34    base_path: Option<BoxPath>,
35    cur_inode: usize,
36    cur_dir: Option<Box<Records<'a>>>,
37}
38
39impl<'a> Records<'a> {
40    pub(crate) fn new(
41        meta: &'a BoxMetadata,
42        inodes: &'a [Inode],
43        base_path: Option<BoxPath>,
44    ) -> Records<'a> {
45        Records {
46            meta,
47            inodes,
48            base_path,
49            cur_inode: 0,
50            cur_dir: None,
51        }
52    }
53}
54
55#[non_exhaustive]
56#[derive(Debug)]
57pub struct RecordsItem<'a> {
58    pub(crate) inode: Inode,
59    pub path: BoxPath,
60    pub record: &'a Record,
61}
62
63impl<'a> Iterator for Records<'a> {
64    type Item = RecordsItem<'a>;
65
66    fn next(&mut self) -> Option<Self::Item> {
67        // If iterating a child iterator, do it here.
68        if let Some(dir) = self.cur_dir.as_mut() {
69            if let Some(record) = dir.next() {
70                return Some(record);
71            }
72
73            // If nothing left, clear it here.
74            self.cur_dir = None;
75        }
76        let inode = match self.inodes.get(self.cur_inode) {
77            Some(i) => *i,
78            None => return None,
79        };
80
81        let record = match self.meta.record(inode) {
82            Some(v) => v,
83            None => return None,
84        };
85
86        let base_path = match self.base_path.as_ref() {
87            Some(x) => x.join_unchecked(record.name()),
88            None => BoxPath(record.name().to_string()),
89        };
90
91        if let Record::Directory(record) = record {
92            self.cur_dir = Some(Box::new(Records::new(
93                self.meta,
94                &*record.inodes,
95                Some(base_path.clone()),
96            )));
97        }
98
99        self.cur_inode += 1;
100        Some(RecordsItem {
101            inode,
102            path: base_path,
103            record,
104        })
105    }
106}
107
108#[derive(Debug)]
109pub struct FindRecord<'a> {
110    meta: &'a BoxMetadata,
111    query: VecDeque<String>,
112    inodes: &'a [Inode],
113}
114
115impl<'a> FindRecord<'a> {
116    pub(crate) fn new(
117        meta: &'a BoxMetadata,
118        query: VecDeque<String>,
119        inodes: &'a [Inode],
120    ) -> FindRecord<'a> {
121        log::debug!("FindRecord query: {:?}", query);
122
123        FindRecord {
124            meta,
125            query,
126            inodes,
127        }
128    }
129}
130
131impl<'a> Iterator for FindRecord<'a> {
132    type Item = Inode;
133
134    fn next(&mut self) -> Option<Self::Item> {
135        let candidate_name = match self.query.pop_front() {
136            Some(v) => v,
137            None => return None,
138        };
139
140        log::debug!("candidate_name: {}", &candidate_name);
141
142        let result = self
143            .inodes
144            .iter()
145            .map(|inode| (*inode, self.meta.record(*inode).unwrap()))
146            .find(|x| x.1.name() == candidate_name);
147
148        match result {
149            Some(v) => {
150                log::debug!("{:?}", v);
151                if self.query.is_empty() {
152                    Some(v.0)
153                } else if let Record::Directory(record) = v.1 {
154                    let mut tmp = VecDeque::new();
155                    std::mem::swap(&mut self.query, &mut tmp);
156                    let result = FindRecord::new(self.meta, tmp, &*record.inodes).next();
157                    log::debug!("FindRecord result: {:?}", &result);
158                    result
159                } else {
160                    None
161                }
162            }
163            None => None,
164        }
165    }
166}
167
168impl BoxMetadata {
169    #[inline(always)]
170    pub fn iter(&self) -> Records {
171        Records::new(self, &*self.root, None)
172    }
173
174    #[inline(always)]
175    pub fn root_records(&self) -> Vec<(Inode, &Record)> {
176        self.root
177            .iter()
178            .copied()
179            .filter_map(|x| self.record(x).map(|r| (x, r)))
180            .collect()
181    }
182
183    #[inline(always)]
184    pub fn records(&self, dir_record: &DirectoryRecord) -> Vec<(Inode, &Record)> {
185        dir_record
186            .inodes
187            .iter()
188            .copied()
189            .filter_map(|x| self.record(x).map(|r| (x, r)))
190            .collect()
191    }
192
193    #[inline(always)]
194    pub fn inode(&self, path: &BoxPath) -> Option<Inode> {
195        // if let Some(inode) = self.index.as_ref().and_then(|x| x.get(path)) {
196        //     return Inode::new(inode).ok();
197        // };
198
199        FindRecord::new(self, path.iter().map(str::to_string).collect(), &*self.root).next()
200    }
201
202    #[inline(always)]
203    pub fn record(&self, inode: Inode) -> Option<&Record> {
204        self.inodes.get(inode.get() as usize - 1)
205    }
206
207    #[inline(always)]
208    pub fn record_mut(&mut self, inode: Inode) -> Option<&mut Record> {
209        self.inodes.get_mut(inode.get() as usize - 1)
210    }
211
212    #[inline(always)]
213    pub fn insert_record(&mut self, record: Record) -> Inode {
214        self.inodes.push(record);
215        Inode::new(self.inodes.len() as u64).unwrap()
216    }
217
218    #[inline(always)]
219    pub fn attr<S: AsRef<str>>(&self, path: &BoxPath, key: S) -> Option<&[u8]> {
220        let key = self.attr_key(key.as_ref())?;
221
222        if let Some(record) = self.inode(path).and_then(|x| self.record(x)) {
223            record.attrs().get(&key).map(|x| &**x)
224        } else {
225            None
226        }
227    }
228
229    pub fn file_attrs(&self) -> BTreeMap<&str, AttrValue<'_>> {
230        let mut map = BTreeMap::new();
231
232        for key in self.attr_keys.iter() {
233            let k = self.attr_key(key).unwrap();
234            if let Some(v) = self.attrs.get(&k) {
235                let value = std::str::from_utf8(v)
236                    .map(|v| {
237                        serde_json::from_str(v)
238                            .map(AttrValue::Json)
239                            .unwrap_or(AttrValue::String(v))
240                    })
241                    .unwrap_or(AttrValue::Bytes(v));
242                map.insert(&**key, value);
243            }
244        }
245
246        map
247    }
248
249    #[inline(always)]
250    pub fn file_attr<S: AsRef<str>>(&self, key: S) -> Option<&Vec<u8>> {
251        let key = self.attr_key(key.as_ref())?;
252
253        self.attrs.get(&key)
254    }
255
256    #[inline(always)]
257    pub fn attr_key(&self, key: &str) -> Option<usize> {
258        self.attr_keys.iter().position(|r| r == key)
259    }
260
261    #[inline(always)]
262    pub fn attr_key_or_create(&mut self, key: &str) -> usize {
263        match self.attr_keys.iter().position(|r| r == key) {
264            Some(v) => v,
265            None => {
266                let len = self.attr_keys.len();
267                self.attr_keys.push(key.to_string());
268                len
269            }
270        }
271    }
272}
273
274#[derive(Debug)]
275pub enum AttrValue<'a> {
276    String(&'a str),
277    Bytes(&'a [u8]),
278    Json(serde_json::Value),
279}
280
281impl AttrValue<'_> {
282    pub fn as_bytes(&self) -> Cow<'_, [u8]> {
283        match self {
284            AttrValue::String(x) => Cow::Borrowed(x.as_bytes()),
285            AttrValue::Bytes(x) => Cow::Borrowed(*x),
286            AttrValue::Json(x) => Cow::Owned(serde_json::to_vec(x).unwrap()),
287        }
288    }
289}
290
291impl Display for AttrValue<'_> {
292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293        match self {
294            AttrValue::String(v) => f.write_str(v),
295            AttrValue::Bytes(bytes) => {
296                let mut bytes = bytes.iter();
297                if let Some(v) = bytes.next() {
298                    f.write_fmt(format_args!("{:02x}", v))?;
299                }
300                for b in bytes {
301                    f.write_fmt(format_args!(" {:02x}", b))?;
302                }
303                Ok(())
304            }
305            AttrValue::Json(value) => {
306                let v = serde_json::to_string_pretty(value).unwrap();
307                f.write_str(&v)
308            }
309        }
310    }
311}