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
24pub 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 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}