1use super::TimeSource;
4use crate::{
5 BlockDevice, Error, RawVolume, VolumeManager,
6 filesystem::{ClusterId, DirEntry, Handle},
7};
8use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write};
9
10#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
27#[derive(Debug, Copy, Clone, PartialEq, Eq)]
28pub struct RawFile(pub(crate) Handle);
29
30impl RawFile {
31 pub fn to_file<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(
33 self,
34 volume_mgr: &VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
35 ) -> File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
36 where
37 D: crate::BlockDevice,
38 T: crate::TimeSource,
39 {
40 File::new(self, volume_mgr)
41 }
42}
43
44pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
53where
54 D: crate::BlockDevice,
55 T: crate::TimeSource,
56{
57 raw_file: RawFile,
58 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
59}
60
61impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
62 File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
63where
64 D: crate::BlockDevice,
65 T: crate::TimeSource,
66{
67 pub fn new(
69 raw_file: RawFile,
70 volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
71 ) -> Self {
72 File {
73 raw_file,
74 volume_mgr,
75 }
76 }
77
78 pub fn read(&self, buffer: &mut [u8]) -> Result<usize, crate::Error<D::Error>> {
85 self.volume_mgr.read(self.raw_file, buffer)
86 }
87
88 pub fn write(&self, buffer: &[u8]) -> Result<(), crate::Error<D::Error>> {
93 self.volume_mgr.write(self.raw_file, buffer)
94 }
95
96 pub fn is_eof(&self) -> bool {
101 self.volume_mgr
102 .file_eof(self.raw_file)
103 .expect("Corrupt file ID")
104 }
105
106 pub fn seek_from_current(&self, offset: i32) -> Result<(), crate::Error<D::Error>> {
111 self.volume_mgr
112 .file_seek_from_current(self.raw_file, offset)
113 }
114
115 pub fn seek_from_start(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
120 self.volume_mgr.file_seek_from_start(self.raw_file, offset)
121 }
122
123 pub fn seek_from_end(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
128 self.volume_mgr.file_seek_from_end(self.raw_file, offset)
129 }
130
131 pub fn length(&self) -> u32 {
136 self.volume_mgr
137 .file_length(self.raw_file)
138 .expect("Corrupt file ID")
139 }
140
141 pub fn offset(&self) -> u32 {
146 self.volume_mgr
147 .file_offset(self.raw_file)
148 .expect("Corrupt file ID")
149 }
150
151 pub fn to_raw_file(self) -> RawFile {
153 let f = self.raw_file;
154 core::mem::forget(self);
155 f
156 }
157
158 pub fn flush(&self) -> Result<(), Error<D::Error>> {
163 self.volume_mgr.flush_file(self.raw_file)
164 }
165
166 pub fn close(self) -> Result<(), Error<D::Error>> {
171 let result = self.volume_mgr.close_file(self.raw_file);
172 core::mem::forget(self);
173 result
174 }
175}
176
177impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
178 for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
179where
180 D: crate::BlockDevice,
181 T: crate::TimeSource,
182{
183 fn drop(&mut self) {
184 _ = self.volume_mgr.close_file(self.raw_file);
185 }
186}
187
188impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
189 core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
190where
191 D: crate::BlockDevice,
192 T: crate::TimeSource,
193{
194 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
195 write!(f, "File({})", self.raw_file.0.0)
196 }
197}
198
199impl<
200 D: BlockDevice,
201 T: TimeSource,
202 const MAX_DIRS: usize,
203 const MAX_FILES: usize,
204 const MAX_VOLUMES: usize,
205> ErrorType for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
206{
207 type Error = crate::Error<D::Error>;
208}
209
210impl<
211 D: BlockDevice,
212 T: TimeSource,
213 const MAX_DIRS: usize,
214 const MAX_FILES: usize,
215 const MAX_VOLUMES: usize,
216> Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
217{
218 fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
219 if buf.is_empty() {
220 Ok(0)
221 } else {
222 File::read(self, buf)
223 }
224 }
225}
226
227impl<
228 D: BlockDevice,
229 T: TimeSource,
230 const MAX_DIRS: usize,
231 const MAX_FILES: usize,
232 const MAX_VOLUMES: usize,
233> Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
234{
235 fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
236 if buf.is_empty() {
237 Ok(0)
238 } else {
239 File::write(self, buf)?;
240 Ok(buf.len())
241 }
242 }
243
244 fn flush(&mut self) -> Result<(), Self::Error> {
245 Self::flush(self)
246 }
247}
248
249impl<
250 D: BlockDevice,
251 T: TimeSource,
252 const MAX_DIRS: usize,
253 const MAX_FILES: usize,
254 const MAX_VOLUMES: usize,
255> Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
256{
257 fn seek(&mut self, pos: SeekFrom) -> Result<u64, Self::Error> {
258 match pos {
259 SeekFrom::Start(offset) => {
260 self.seek_from_start(offset.try_into().map_err(|_| Error::InvalidOffset)?)?
261 }
262 SeekFrom::End(offset) => {
263 self.seek_from_end((-offset).try_into().map_err(|_| Error::InvalidOffset)?)?
264 }
265 SeekFrom::Current(offset) => {
266 self.seek_from_current(offset.try_into().map_err(|_| Error::InvalidOffset)?)?
267 }
268 }
269 Ok(self.offset().into())
270 }
271}
272
273#[cfg(feature = "defmt-log")]
274impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
275 defmt::Format for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
276where
277 D: crate::BlockDevice,
278 T: crate::TimeSource,
279{
280 fn format(&self, fmt: defmt::Formatter) {
281 defmt::write!(fmt, "File({})", self.raw_file.0.0)
282 }
283}
284
285#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
287#[derive(Debug, Clone, Copy, PartialEq, Eq)]
288pub enum FileError {
289 InvalidOffset,
291}
292
293#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
295#[derive(Debug, PartialEq, Eq, Copy, Clone)]
296pub enum Mode {
297 ReadOnly,
299 ReadWriteAppend,
301 ReadWriteTruncate,
303 ReadWriteCreate,
305 ReadWriteCreateOrTruncate,
307 ReadWriteCreateOrAppend,
309}
310
311#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
313#[derive(Debug, Clone)]
314pub(crate) struct FileInfo {
315 pub(crate) raw_file: RawFile,
317 pub(crate) raw_volume: RawVolume,
319 pub(crate) current_cluster: (u32, ClusterId),
324 pub(crate) current_offset: u32,
326 pub(crate) mode: Mode,
328 pub(crate) entry: DirEntry,
330 pub(crate) dirty: bool,
332}
333
334impl FileInfo {
335 pub fn eof(&self) -> bool {
337 self.current_offset == self.entry.size
338 }
339
340 pub fn length(&self) -> u32 {
342 self.entry.size
343 }
344
345 pub fn seek_from_start(&mut self, offset: u32) -> Result<(), FileError> {
347 if offset > self.entry.size {
348 return Err(FileError::InvalidOffset);
349 }
350 self.current_offset = offset;
351 Ok(())
352 }
353
354 pub fn seek_from_end(&mut self, offset: u32) -> Result<(), FileError> {
356 if offset > self.entry.size {
357 return Err(FileError::InvalidOffset);
358 }
359 self.current_offset = self.entry.size - offset;
360 Ok(())
361 }
362
363 pub fn seek_from_current(&mut self, offset: i32) -> Result<(), FileError> {
365 let new_offset = i64::from(self.current_offset) + i64::from(offset);
366 if new_offset < 0 || new_offset > i64::from(self.entry.size) {
367 return Err(FileError::InvalidOffset);
368 }
369 self.current_offset = new_offset as u32;
370 Ok(())
371 }
372
373 pub fn left(&self) -> u32 {
375 self.entry.size - self.current_offset
376 }
377
378 pub(crate) fn update_length(&mut self, new: u32) {
380 self.entry.size = new;
381 }
382}
383
384