Skip to main content

littlefs_rust/
dir.rs

1use alloc::boxed::Box;
2use alloc::vec::Vec;
3use core::mem::MaybeUninit;
4
5use littlefs_rust_core::{LfsDir, LfsInfo};
6
7use crate::error::{from_lfs_result, Error};
8use crate::filesystem::Filesystem;
9use crate::metadata::{DirEntry, FileType};
10use crate::storage::Storage;
11
12pub(crate) struct DirAllocation {
13    pub(crate) dir: MaybeUninit<LfsDir>,
14}
15
16impl DirAllocation {
17    pub(crate) fn new() -> Self {
18        Self {
19            dir: MaybeUninit::zeroed(),
20        }
21    }
22}
23
24/// An open directory iterator.
25///
26/// Obtained from [`Filesystem::read_dir`]. Yields [`DirEntry`] items,
27/// automatically skipping `.` and `..`. Closed on drop, or explicitly
28/// via [`ReadDir::close`].
29pub struct ReadDir<'a, S: Storage> {
30    fs: &'a Filesystem<S>,
31    alloc: Box<DirAllocation>,
32    closed: bool,
33}
34
35impl<'a, S: Storage> ReadDir<'a, S> {
36    pub(crate) fn open(fs: &'a Filesystem<S>, path: &str) -> Result<Self, Error> {
37        let mut alloc = Box::new(DirAllocation::new());
38        let path_bytes = null_terminate(path);
39        {
40            let mut inner = fs.inner.borrow_mut();
41            let rc = littlefs_rust_core::lfs_dir_open(
42                inner.lfs.as_mut_ptr(),
43                alloc.dir.as_mut_ptr(),
44                path_bytes.as_ptr(),
45            );
46            from_lfs_result(rc)?;
47        }
48        Ok(ReadDir {
49            fs,
50            alloc,
51            closed: false,
52        })
53    }
54
55    /// Close the directory handle. Consumes `self`.
56    ///
57    /// Dropping a [`ReadDir`] also closes it, but errors are silently ignored.
58    pub fn close(mut self) -> Result<(), Error> {
59        self.closed = true;
60        let mut inner = self.fs.inner.borrow_mut();
61        let rc =
62            littlefs_rust_core::lfs_dir_close(inner.lfs.as_mut_ptr(), self.alloc.dir.as_mut_ptr());
63        from_lfs_result(rc)
64    }
65}
66
67impl<S: Storage> Iterator for ReadDir<'_, S> {
68    type Item = Result<DirEntry, Error>;
69
70    fn next(&mut self) -> Option<Self::Item> {
71        loop {
72            let mut info = MaybeUninit::<LfsInfo>::zeroed();
73            let rc = {
74                let mut inner = self.fs.inner.borrow_mut();
75                littlefs_rust_core::lfs_dir_read(
76                    inner.lfs.as_mut_ptr(),
77                    self.alloc.dir.as_mut_ptr(),
78                    info.as_mut_ptr(),
79                )
80            };
81
82            return match rc {
83                0 => None,
84                n if n < 0 => Some(Err(Error::from(n))),
85                _ => {
86                    let entry = dir_entry_from_info(unsafe { &*info.as_ptr() });
87                    if entry.name == "." || entry.name == ".." {
88                        continue;
89                    }
90                    Some(Ok(entry))
91                }
92            };
93        }
94    }
95}
96
97impl<S: Storage> Drop for ReadDir<'_, S> {
98    fn drop(&mut self) {
99        if !self.closed {
100            if let Ok(mut inner) = self.fs.inner.try_borrow_mut() {
101                let _ = littlefs_rust_core::lfs_dir_close(
102                    inner.lfs.as_mut_ptr(),
103                    self.alloc.dir.as_mut_ptr(),
104                );
105            }
106        }
107    }
108}
109
110pub(crate) fn dir_entry_from_info(info: &LfsInfo) -> DirEntry {
111    let nul = info
112        .name
113        .iter()
114        .position(|&b| b == 0)
115        .unwrap_or(info.name.len());
116    let name = core::str::from_utf8(&info.name[..nul]).unwrap_or("").into();
117    let file_type = if info.type_ == littlefs_rust_core::lfs_type::lfs_type::LFS_TYPE_DIR as u8 {
118        FileType::Dir
119    } else {
120        FileType::File
121    };
122    DirEntry {
123        name,
124        file_type,
125        size: info.size,
126    }
127}
128
129fn null_terminate(s: &str) -> Vec<u8> {
130    let mut v: Vec<u8> = s.bytes().collect();
131    v.push(0);
132    v
133}