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