1use core::ops::ControlFlow;
4
5use crate::blockdevice::BlockIdx;
6use crate::fat::{FatType, OnDiskDirEntry};
7use crate::filesystem::{Attributes, ClusterId, Handle, LfnBuffer, ShortFileName, Timestamp};
8use crate::{Error, RawVolume, VolumeManager};
9
10use super::ToShortFileName;
11
12#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
14#[derive(Debug, PartialEq, Eq, Clone)]
15pub struct DirEntry {
16 pub name: ShortFileName,
18 pub mtime: Timestamp,
20 pub ctime: Timestamp,
22 pub attributes: Attributes,
24 pub cluster: ClusterId,
26 pub size: u32,
28 pub entry_block: BlockIdx,
30 pub entry_offset: u32,
32}
33
34#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
51#[derive(Debug, Copy, Clone, PartialEq, Eq)]
52pub struct RawDirectory(pub(crate) Handle);
53
54impl RawDirectory {
55 pub fn to_directory<
57 D,
58 T,
59 const MAX_DIRS: usize,
60 const MAX_FILES: usize,
61 const MAX_VOLUMES: usize,
62 >(
63 self,
64 volume_mgr: &VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
65 ) -> Directory<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
66 where
67 D: crate::BlockDevice,
68 T: crate::TimeSource,
69 {
70 Directory::new(self, volume_mgr)
71 }
72}
73
74pub struct Directory<
83 'a,
84 D,
85 T,
86 const MAX_DIRS: usize,
87 const MAX_FILES: usize,
88 const MAX_VOLUMES: usize,
89> where
90 D: crate::BlockDevice,
91 T: crate::TimeSource,
92{
93 raw_directory: RawDirectory,
94 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
95}
96
97impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
98 Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
99where
100 D: crate::BlockDevice,
101 T: crate::TimeSource,
102{
103 pub fn new(
105 raw_directory: RawDirectory,
106 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
107 ) -> Self {
108 Directory {
109 raw_directory,
110 volume_mgr,
111 }
112 }
113
114 pub fn open_dir<N>(&self, name: N) -> Result<Self, Error<D::Error>>
121 where
122 N: ToShortFileName,
123 {
124 let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
125 Ok(d.to_directory(self.volume_mgr))
126 }
127
128 pub fn change_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
132 where
133 N: ToShortFileName,
134 {
135 let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
136 self.volume_mgr.close_dir(self.raw_directory).unwrap();
137 self.raw_directory = d;
138 Ok(())
139 }
140
141 pub fn find_directory_entry<N>(&self, name: N) -> Result<DirEntry, Error<D::Error>>
146 where
147 N: ToShortFileName,
148 {
149 self.volume_mgr
150 .find_directory_entry(self.raw_directory, name)
151 }
152
153 pub fn directory_entry_exists<N: ToShortFileName>(&self, name: N) -> bool {
155 self.find_directory_entry(name).is_ok()
156 }
157
158 pub fn iterate_dir<F>(&self, func: F) -> Result<(), Error<D::Error>>
165 where
166 F: FnMut(&DirEntry) -> ControlFlow<()>,
167 {
168 self.volume_mgr.iterate_dir(self.raw_directory, func)
169 }
170
171 pub fn iterate_dir_lfn<F>(
177 &self,
178 lfn_buffer: &mut LfnBuffer<'_>,
179 func: F,
180 ) -> Result<(), Error<D::Error>>
181 where
182 F: FnMut(&DirEntry, Option<&str>) -> ControlFlow<()>,
183 {
184 self.volume_mgr
185 .iterate_dir_lfn(self.raw_directory, lfn_buffer, func)
186 }
187
188 pub fn open_file_in_dir<N>(
193 &self,
194 name: N,
195 mode: crate::Mode,
196 ) -> Result<crate::File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, crate::Error<D::Error>>
197 where
198 N: super::ToShortFileName,
199 {
200 let f = self
201 .volume_mgr
202 .open_file_in_dir(self.raw_directory, name, mode)?;
203 Ok(f.to_file(self.volume_mgr))
204 }
205
206 pub fn open_long_name_file_in_dir(
211 &self,
212 name: &str,
213 mode: crate::Mode,
214 ) -> Result<crate::File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, crate::Error<D::Error>>
215 {
216 let f = self
217 .volume_mgr
218 .open_long_name_file_in_dir(self.raw_directory, name, mode)?;
219 Ok(f.to_file(self.volume_mgr))
220 }
221
222 pub fn delete_entry_in_dir<N>(&self, name: N) -> Result<(), Error<D::Error>>
227 where
228 N: ToShortFileName,
229 {
230 self.volume_mgr
231 .delete_entry_in_dir(self.raw_directory, name)
232 }
233
234 pub fn make_dir_in_dir<N>(&self, name: N) -> Result<(), Error<D::Error>>
239 where
240 N: ToShortFileName,
241 {
242 self.volume_mgr.make_dir_in_dir(self.raw_directory, name)
243 }
244
245 pub fn to_raw_directory(self) -> RawDirectory {
247 let d = self.raw_directory;
248 core::mem::forget(self);
249 d
250 }
251
252 pub fn close(self) -> Result<(), Error<D::Error>> {
257 let result = self.volume_mgr.close_dir(self.raw_directory);
258 core::mem::forget(self);
259 result
260 }
261}
262
263impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
264 for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
265where
266 D: crate::BlockDevice,
267 T: crate::TimeSource,
268{
269 fn drop(&mut self) {
270 _ = self.volume_mgr.close_dir(self.raw_directory)
271 }
272}
273
274impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
275 core::fmt::Debug for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
276where
277 D: crate::BlockDevice,
278 T: crate::TimeSource,
279{
280 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
281 write!(f, "Directory({})", self.raw_directory.0.0)
282 }
283}
284
285#[cfg(feature = "defmt-log")]
286impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
287 defmt::Format for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
288where
289 D: crate::BlockDevice,
290 T: crate::TimeSource,
291{
292 fn format(&self, fmt: defmt::Formatter) {
293 defmt::write!(fmt, "Directory({})", self.raw_directory.0.0)
294 }
295}
296
297#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
299#[derive(Debug, Clone)]
300pub(crate) struct DirectoryInfo {
301 pub(crate) raw_directory: RawDirectory,
303 pub(crate) raw_volume: RawVolume,
305 pub(crate) cluster: ClusterId,
307}
308
309impl DirEntry {
310 pub(crate) fn serialize(&self, fat_type: FatType) -> [u8; OnDiskDirEntry::LEN] {
311 let mut data = [0u8; OnDiskDirEntry::LEN];
312 data[0..11].copy_from_slice(&self.name.contents);
313 data[11] = self.attributes.0;
314 data[14..18].copy_from_slice(&self.ctime.serialize_to_fat()[..]);
317 let cluster_number = self.cluster.0;
319 let cluster_hi = if fat_type == FatType::Fat16 {
320 [0u8; 2]
321 } else {
322 (((cluster_number >> 16) & 0x0000_FFFF) as u16).to_le_bytes()
324 };
325 data[20..22].copy_from_slice(&cluster_hi[..]);
326 data[22..26].copy_from_slice(&self.mtime.serialize_to_fat()[..]);
327 let cluster_lo = ((cluster_number & 0x0000_FFFF) as u16).to_le_bytes();
329 data[26..28].copy_from_slice(&cluster_lo[..]);
330 data[28..32].copy_from_slice(&self.size.to_le_bytes()[..]);
331 data
332 }
333
334 pub(crate) fn new(
335 name: ShortFileName,
336 attributes: Attributes,
337 cluster: ClusterId,
338 ctime: Timestamp,
339 entry_block: BlockIdx,
340 entry_offset: u32,
341 ) -> Self {
342 Self {
343 name,
344 mtime: ctime,
345 ctime,
346 attributes,
347 cluster,
348 size: 0,
349 entry_block,
350 entry_offset,
351 }
352 }
353}
354
355