1use core::convert::TryFrom;
2
3use crate::blockdevice::BlockIdx;
4use crate::fat::{FatType, OnDiskDirEntry};
5use crate::filesystem::{Attributes, ClusterId, SearchId, ShortFileName, Timestamp};
6use crate::{Error, RawVolume, VolumeManager};
7
8use super::ToShortFileName;
9
10#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
12#[derive(Debug, PartialEq, Eq, Clone)]
13pub struct DirEntry {
14 pub name: ShortFileName,
16 pub mtime: Timestamp,
18 pub ctime: Timestamp,
20 pub attributes: Attributes,
22 pub cluster: ClusterId,
24 pub size: u32,
26 pub entry_block: BlockIdx,
28 pub entry_offset: u32,
30}
31
32#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
49#[derive(Debug, Copy, Clone, PartialEq, Eq)]
50pub struct RawDirectory(pub(crate) SearchId);
51
52impl RawDirectory {
53 pub fn to_directory<
55 D,
56 T,
57 const MAX_DIRS: usize,
58 const MAX_FILES: usize,
59 const MAX_VOLUMES: usize,
60 >(
61 self,
62 volume_mgr: &mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
63 ) -> Directory<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
64 where
65 D: crate::BlockDevice,
66 T: crate::TimeSource,
67 {
68 Directory::new(self, volume_mgr)
69 }
70}
71
72pub struct Directory<
81 'a,
82 D,
83 T,
84 const MAX_DIRS: usize,
85 const MAX_FILES: usize,
86 const MAX_VOLUMES: usize,
87> where
88 D: crate::BlockDevice,
89 T: crate::TimeSource,
90{
91 raw_directory: RawDirectory,
92 volume_mgr: &'a mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
93}
94
95impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
96 Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
97where
98 D: crate::BlockDevice,
99 T: crate::TimeSource,
100{
101 pub fn new(
103 raw_directory: RawDirectory,
104 volume_mgr: &'a mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
105 ) -> Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
106 Directory {
107 raw_directory,
108 volume_mgr,
109 }
110 }
111
112 pub fn open_dir<N>(
116 &mut self,
117 name: N,
118 ) -> Result<Directory<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>>
119 where
120 N: ToShortFileName,
121 {
122 let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
123 Ok(d.to_directory(self.volume_mgr))
124 }
125
126 pub fn change_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
130 where
131 N: ToShortFileName,
132 {
133 let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
134 self.volume_mgr.close_dir(self.raw_directory).unwrap();
135 self.raw_directory = d;
136 Ok(())
137 }
138
139 pub fn find_directory_entry<N>(&mut self, name: N) -> Result<DirEntry, Error<D::Error>>
141 where
142 N: ToShortFileName,
143 {
144 self.volume_mgr
145 .find_directory_entry(self.raw_directory, name)
146 }
147
148 pub fn iterate_dir<F>(&mut self, func: F) -> Result<(), Error<D::Error>>
150 where
151 F: FnMut(&DirEntry),
152 {
153 self.volume_mgr.iterate_dir(self.raw_directory, func)
154 }
155
156 pub fn open_file_in_dir<N>(
158 &mut self,
159 name: N,
160 mode: crate::Mode,
161 ) -> Result<crate::File<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, crate::Error<D::Error>>
162 where
163 N: super::ToShortFileName,
164 {
165 let f = self
166 .volume_mgr
167 .open_file_in_dir(self.raw_directory, name, mode)?;
168 Ok(f.to_file(self.volume_mgr))
169 }
170
171 pub fn delete_file_in_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
173 where
174 N: ToShortFileName,
175 {
176 self.volume_mgr.delete_file_in_dir(self.raw_directory, name)
177 }
178
179 pub fn make_dir_in_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
181 where
182 N: ToShortFileName,
183 {
184 self.volume_mgr.make_dir_in_dir(self.raw_directory, name)
185 }
186
187 pub fn to_raw_directory(self) -> RawDirectory {
189 let d = self.raw_directory;
190 core::mem::forget(self);
191 d
192 }
193
194 pub fn close(self) -> Result<(), Error<D::Error>> {
199 let result = self.volume_mgr.close_dir(self.raw_directory);
200 core::mem::forget(self);
201 result
202 }
203}
204
205impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
206 for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
207where
208 D: crate::BlockDevice,
209 T: crate::TimeSource,
210{
211 fn drop(&mut self) {
212 _ = self.volume_mgr.close_dir(self.raw_directory)
213 }
214}
215
216impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
217 core::fmt::Debug for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
218where
219 D: crate::BlockDevice,
220 T: crate::TimeSource,
221{
222 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
223 write!(f, "Directory({})", self.raw_directory.0 .0)
224 }
225}
226
227#[cfg(feature = "defmt-log")]
228impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
229 defmt::Format for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
230where
231 D: crate::BlockDevice,
232 T: crate::TimeSource,
233{
234 fn format(&self, fmt: defmt::Formatter) {
235 defmt::write!(fmt, "Directory({})", self.raw_directory.0 .0)
236 }
237}
238
239#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
241#[derive(Debug, Clone)]
242pub(crate) struct DirectoryInfo {
243 pub(crate) directory_id: RawDirectory,
245 pub(crate) volume_id: RawVolume,
247 pub(crate) cluster: ClusterId,
249}
250
251impl DirEntry {
252 pub(crate) fn serialize(&self, fat_type: FatType) -> [u8; OnDiskDirEntry::LEN] {
253 let mut data = [0u8; OnDiskDirEntry::LEN];
254 data[0..11].copy_from_slice(&self.name.contents);
255 data[11] = self.attributes.0;
256 data[14..18].copy_from_slice(&self.ctime.serialize_to_fat()[..]);
259 let cluster_number = self.cluster.0;
261 let cluster_hi = if fat_type == FatType::Fat16 {
262 [0u8; 2]
263 } else {
264 u16::try_from((cluster_number >> 16) & 0x0000_FFFF)
266 .unwrap()
267 .to_le_bytes()
268 };
269 data[20..22].copy_from_slice(&cluster_hi[..]);
270 data[22..26].copy_from_slice(&self.mtime.serialize_to_fat()[..]);
271 let cluster_lo = u16::try_from(cluster_number & 0x0000_FFFF)
273 .unwrap()
274 .to_le_bytes();
275 data[26..28].copy_from_slice(&cluster_lo[..]);
276 data[28..32].copy_from_slice(&self.size.to_le_bytes()[..]);
277 data
278 }
279
280 pub(crate) fn new(
281 name: ShortFileName,
282 attributes: Attributes,
283 cluster: ClusterId,
284 ctime: Timestamp,
285 entry_block: BlockIdx,
286 entry_offset: u32,
287 ) -> Self {
288 Self {
289 name,
290 mtime: ctime,
291 ctime,
292 attributes,
293 cluster,
294 size: 0,
295 entry_block,
296 entry_offset,
297 }
298 }
299}
300
301