embedded_sdmmc/filesystem/
files.rs1use crate::{
2    filesystem::{ClusterId, DirEntry, SearchId},
3    Error, RawVolume, VolumeManager,
4};
5
6#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
23#[derive(Debug, Copy, Clone, PartialEq, Eq)]
24pub struct RawFile(pub(crate) SearchId);
25
26impl RawFile {
27    pub fn to_file<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(
29        self,
30        volume_mgr: &mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
31    ) -> File<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
32    where
33        D: crate::BlockDevice,
34        T: crate::TimeSource,
35    {
36        File::new(self, volume_mgr)
37    }
38}
39
40pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
49where
50    D: crate::BlockDevice,
51    T: crate::TimeSource,
52{
53    raw_file: RawFile,
54    volume_mgr: &'a mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
55}
56
57impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
58    File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
59where
60    D: crate::BlockDevice,
61    T: crate::TimeSource,
62{
63    pub fn new(
65        raw_file: RawFile,
66        volume_mgr: &'a mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
67    ) -> File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
68        File {
69            raw_file,
70            volume_mgr,
71        }
72    }
73
74    pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, crate::Error<D::Error>> {
78        self.volume_mgr.read(self.raw_file, buffer)
79    }
80
81    pub fn write(&mut self, buffer: &[u8]) -> Result<(), crate::Error<D::Error>> {
83        self.volume_mgr.write(self.raw_file, buffer)
84    }
85
86    pub fn is_eof(&self) -> bool {
88        self.volume_mgr
89            .file_eof(self.raw_file)
90            .expect("Corrupt file ID")
91    }
92
93    pub fn seek_from_current(&mut self, offset: i32) -> Result<(), crate::Error<D::Error>> {
95        self.volume_mgr
96            .file_seek_from_current(self.raw_file, offset)
97    }
98
99    pub fn seek_from_start(&mut self, offset: u32) -> Result<(), crate::Error<D::Error>> {
101        self.volume_mgr.file_seek_from_start(self.raw_file, offset)
102    }
103
104    pub fn seek_from_end(&mut self, offset: u32) -> Result<(), crate::Error<D::Error>> {
106        self.volume_mgr.file_seek_from_end(self.raw_file, offset)
107    }
108
109    pub fn length(&self) -> u32 {
111        self.volume_mgr
112            .file_length(self.raw_file)
113            .expect("Corrupt file ID")
114    }
115
116    pub fn offset(&self) -> u32 {
118        self.volume_mgr
119            .file_offset(self.raw_file)
120            .expect("Corrupt file ID")
121    }
122
123    pub fn to_raw_file(self) -> RawFile {
125        let f = self.raw_file;
126        core::mem::forget(self);
127        f
128    }
129
130    pub fn flush(&mut self) -> Result<(), Error<D::Error>> {
132        self.volume_mgr.flush_file(self.raw_file)
133    }
134
135    pub fn close(self) -> Result<(), Error<D::Error>> {
140        let result = self.volume_mgr.close_file(self.raw_file);
141        core::mem::forget(self);
142        result
143    }
144}
145
146impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
147    for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
148where
149    D: crate::BlockDevice,
150    T: crate::TimeSource,
151{
152    fn drop(&mut self) {
153        _ = self.volume_mgr.close_file(self.raw_file);
154    }
155}
156
157impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
158    core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
159where
160    D: crate::BlockDevice,
161    T: crate::TimeSource,
162{
163    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
164        write!(f, "File({})", self.raw_file.0 .0)
165    }
166}
167
168#[cfg(feature = "defmt-log")]
169impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
170    defmt::Format for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
171where
172    D: crate::BlockDevice,
173    T: crate::TimeSource,
174{
175    fn format(&self, fmt: defmt::Formatter) {
176        defmt::write!(fmt, "File({})", self.raw_file.0 .0)
177    }
178}
179
180#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
182#[derive(Debug, Clone, Copy, PartialEq, Eq)]
183pub enum FileError {
184    InvalidOffset,
186}
187
188#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
190#[derive(Debug, PartialEq, Eq, Copy, Clone)]
191pub enum Mode {
192    ReadOnly,
194    ReadWriteAppend,
196    ReadWriteTruncate,
198    ReadWriteCreate,
200    ReadWriteCreateOrTruncate,
202    ReadWriteCreateOrAppend,
204}
205
206#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
208#[derive(Debug, Clone)]
209pub(crate) struct FileInfo {
210    pub(crate) file_id: RawFile,
212    pub(crate) volume_id: RawVolume,
214    pub(crate) current_cluster: (u32, ClusterId),
219    pub(crate) current_offset: u32,
221    pub(crate) mode: Mode,
223    pub(crate) entry: DirEntry,
225    pub(crate) dirty: bool,
227}
228
229impl FileInfo {
230    pub fn eof(&self) -> bool {
232        self.current_offset == self.entry.size
233    }
234
235    pub fn length(&self) -> u32 {
237        self.entry.size
238    }
239
240    pub fn seek_from_start(&mut self, offset: u32) -> Result<(), FileError> {
242        if offset > self.entry.size {
243            return Err(FileError::InvalidOffset);
244        }
245        self.current_offset = offset;
246        Ok(())
247    }
248
249    pub fn seek_from_end(&mut self, offset: u32) -> Result<(), FileError> {
251        if offset > self.entry.size {
252            return Err(FileError::InvalidOffset);
253        }
254        self.current_offset = self.entry.size - offset;
255        Ok(())
256    }
257
258    pub fn seek_from_current(&mut self, offset: i32) -> Result<(), FileError> {
260        let new_offset = i64::from(self.current_offset) + i64::from(offset);
261        if new_offset < 0 || new_offset > i64::from(self.entry.size) {
262            return Err(FileError::InvalidOffset);
263        }
264        self.current_offset = new_offset as u32;
265        Ok(())
266    }
267
268    pub fn left(&self) -> u32 {
270        self.entry.size - self.current_offset
271    }
272
273    pub(crate) fn update_length(&mut self, new: u32) {
275        self.entry.size = new;
276    }
277}
278
279