1use super::TimeSource;
2use crate::{
3 filesystem::{ClusterId, DirEntry, Handle},
4 BlockDevice, Error, RawVolume, VolumeManager,
5};
6use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write};
7
8#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
25#[derive(Debug, Copy, Clone, PartialEq, Eq)]
26pub struct RawFile(pub(crate) Handle);
27
28impl RawFile {
29 pub fn to_file<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(
31 self,
32 volume_mgr: &VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
33 ) -> File<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
34 where
35 D: crate::BlockDevice,
36 T: crate::TimeSource,
37 {
38 File::new(self, volume_mgr)
39 }
40}
41
42pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
51where
52 D: crate::BlockDevice,
53 T: crate::TimeSource,
54{
55 raw_file: RawFile,
56 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
57}
58
59impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
60 File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
61where
62 D: crate::BlockDevice,
63 T: crate::TimeSource,
64{
65 pub fn new(
67 raw_file: RawFile,
68 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
69 ) -> File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
70 File {
71 raw_file,
72 volume_mgr,
73 }
74 }
75
76 pub fn read(&self, buffer: &mut [u8]) -> Result<usize, crate::Error<D::Error>> {
80 self.volume_mgr.read(self.raw_file, buffer)
81 }
82
83 pub fn write(&self, buffer: &[u8]) -> Result<(), crate::Error<D::Error>> {
85 self.volume_mgr.write(self.raw_file, buffer)
86 }
87
88 pub fn is_eof(&self) -> bool {
90 self.volume_mgr
91 .file_eof(self.raw_file)
92 .expect("Corrupt file ID")
93 }
94
95 pub fn seek_from_current(&self, offset: i32) -> Result<(), crate::Error<D::Error>> {
97 self.volume_mgr
98 .file_seek_from_current(self.raw_file, offset)
99 }
100
101 pub fn seek_from_start(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
103 self.volume_mgr.file_seek_from_start(self.raw_file, offset)
104 }
105
106 pub fn seek_from_end(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
108 self.volume_mgr.file_seek_from_end(self.raw_file, offset)
109 }
110
111 pub fn length(&self) -> u32 {
113 self.volume_mgr
114 .file_length(self.raw_file)
115 .expect("Corrupt file ID")
116 }
117
118 pub fn offset(&self) -> u32 {
120 self.volume_mgr
121 .file_offset(self.raw_file)
122 .expect("Corrupt file ID")
123 }
124
125 pub fn to_raw_file(self) -> RawFile {
127 let f = self.raw_file;
128 core::mem::forget(self);
129 f
130 }
131
132 pub fn flush(&self) -> Result<(), Error<D::Error>> {
134 self.volume_mgr.flush_file(self.raw_file)
135 }
136
137 pub fn close(self) -> Result<(), Error<D::Error>> {
142 let result = self.volume_mgr.close_file(self.raw_file);
143 core::mem::forget(self);
144 result
145 }
146}
147
148impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
149 for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
150where
151 D: crate::BlockDevice,
152 T: crate::TimeSource,
153{
154 fn drop(&mut self) {
155 _ = self.volume_mgr.close_file(self.raw_file);
156 }
157}
158
159impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
160 core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
161where
162 D: crate::BlockDevice,
163 T: crate::TimeSource,
164{
165 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
166 write!(f, "File({})", self.raw_file.0 .0)
167 }
168}
169
170impl<
171 D: BlockDevice,
172 T: TimeSource,
173 const MAX_DIRS: usize,
174 const MAX_FILES: usize,
175 const MAX_VOLUMES: usize,
176 > ErrorType for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
177{
178 type Error = crate::Error<D::Error>;
179}
180
181impl<
182 D: BlockDevice,
183 T: TimeSource,
184 const MAX_DIRS: usize,
185 const MAX_FILES: usize,
186 const MAX_VOLUMES: usize,
187 > Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
188{
189 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
190 if buf.is_empty() {
191 Ok(0)
192 } else {
193 self.read(buf)
194 }
195 }
196}
197
198impl<
199 D: BlockDevice,
200 T: TimeSource,
201 const MAX_DIRS: usize,
202 const MAX_FILES: usize,
203 const MAX_VOLUMES: usize,
204 > Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
205{
206 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
207 if buf.is_empty() {
208 Ok(0)
209 } else {
210 self.write(buf)?;
211 Ok(buf.len())
212 }
213 }
214
215 fn flush(&mut self) -> Result<(), Self::Error> {
216 Self::flush(self)
217 }
218}
219
220impl<
221 D: BlockDevice,
222 T: TimeSource,
223 const MAX_DIRS: usize,
224 const MAX_FILES: usize,
225 const MAX_VOLUMES: usize,
226 > Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
227{
228 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
229 match pos {
230 SeekFrom::Start(offset) => {
231 self.seek_from_start(offset.try_into().map_err(|_| Error::InvalidOffset)?)?
232 }
233 SeekFrom::End(offset) => {
234 self.seek_from_end((-offset).try_into().map_err(|_| Error::InvalidOffset)?)?
235 }
236 SeekFrom::Current(offset) => {
237 self.seek_from_current(offset.try_into().map_err(|_| Error::InvalidOffset)?)?
238 }
239 }
240 Ok(self.offset().into())
241 }
242}
243
244#[cfg(feature = "defmt-log")]
245impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
246 defmt::Format for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
247where
248 D: crate::BlockDevice,
249 T: crate::TimeSource,
250{
251 fn format(&self, fmt: defmt::Formatter) {
252 defmt::write!(fmt, "File({})", self.raw_file.0 .0)
253 }
254}
255
256#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
258#[derive(Debug, Clone, Copy, PartialEq, Eq)]
259pub enum FileError {
260 InvalidOffset,
262}
263
264#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
266#[derive(Debug, PartialEq, Eq, Copy, Clone)]
267pub enum Mode {
268 ReadOnly,
270 ReadWriteAppend,
272 ReadWriteTruncate,
274 ReadWriteCreate,
276 ReadWriteCreateOrTruncate,
278 ReadWriteCreateOrAppend,
280}
281
282#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
284#[derive(Debug, Clone)]
285pub(crate) struct FileInfo {
286 pub(crate) raw_file: RawFile,
288 pub(crate) raw_volume: RawVolume,
290 pub(crate) current_cluster: (u32, ClusterId),
295 pub(crate) current_offset: u32,
297 pub(crate) mode: Mode,
299 pub(crate) entry: DirEntry,
301 pub(crate) dirty: bool,
303}
304
305impl FileInfo {
306 pub fn eof(&self) -> bool {
308 self.current_offset == self.entry.size
309 }
310
311 pub fn length(&self) -> u32 {
313 self.entry.size
314 }
315
316 pub fn seek_from_start(&mut self, offset: u32) -> Result<(), FileError> {
318 if offset > self.entry.size {
319 return Err(FileError::InvalidOffset);
320 }
321 self.current_offset = offset;
322 Ok(())
323 }
324
325 pub fn seek_from_end(&mut self, offset: u32) -> Result<(), FileError> {
327 if offset > self.entry.size {
328 return Err(FileError::InvalidOffset);
329 }
330 self.current_offset = self.entry.size - offset;
331 Ok(())
332 }
333
334 pub fn seek_from_current(&mut self, offset: i32) -> Result<(), FileError> {
336 let new_offset = i64::from(self.current_offset) + i64::from(offset);
337 if new_offset < 0 || new_offset > i64::from(self.entry.size) {
338 return Err(FileError::InvalidOffset);
339 }
340 self.current_offset = new_offset as u32;
341 Ok(())
342 }
343
344 pub fn left(&self) -> u32 {
346 self.entry.size - self.current_offset
347 }
348
349 pub(crate) fn update_length(&mut self, new: u32) {
351 self.entry.size = new;
352 }
353}
354
355