diff --git a/CHANGELOG.md b/CHANGELOG.md index ecb40f3..3361d1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,7 +12,7 @@ The format is based on [Keep a Changelog] and this project adheres to [Semantic ### Added -- None +- `File` now implements the `embedded-io` `Read`, `Write` and `Seek` traits. ### Removed diff --git a/Cargo.toml b/Cargo.toml index 88412f7..45276c1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ version = "0.8.0" byteorder = {version = "1", default-features = false} defmt = {version = "0.3", optional = true} embedded-hal = "1.0.0" +embedded-io = "0.6.1" heapless = "^0.8" log = {version = "0.4", default-features = false, optional = true} diff --git a/src/filesystem/files.rs b/src/filesystem/files.rs index 5e52477..1b13e09 100644 --- a/src/filesystem/files.rs +++ b/src/filesystem/files.rs @@ -1,7 +1,9 @@ +use super::TimeSource; use crate::{ filesystem::{ClusterId, DirEntry, SearchId}, - Error, RawVolume, VolumeManager, + BlockDevice, Error, RawVolume, VolumeManager, }; +use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write}; /// A handle for an open file on disk. /// @@ -165,6 +167,80 @@ where } } +impl< + D: BlockDevice, + T: TimeSource, + const MAX_DIRS: usize, + const MAX_FILES: usize, + const MAX_VOLUMES: usize, + > ErrorType for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +{ + type Error = crate::Error; +} + +impl< + D: BlockDevice, + T: TimeSource, + const MAX_DIRS: usize, + const MAX_FILES: usize, + const MAX_VOLUMES: usize, + > Read for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +{ + fn read(&mut self, buf: &mut [u8]) -> Result { + if buf.is_empty() { + Ok(0) + } else { + self.read(buf) + } + } +} + +impl< + D: BlockDevice, + T: TimeSource, + const MAX_DIRS: usize, + const MAX_FILES: usize, + const MAX_VOLUMES: usize, + > Write for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +{ + fn write(&mut self, buf: &[u8]) -> Result { + if buf.is_empty() { + Ok(0) + } else { + self.write(buf)?; + Ok(buf.len()) + } + } + + fn flush(&mut self) -> Result<(), Self::Error> { + self.flush() + } +} + +impl< + D: BlockDevice, + T: TimeSource, + const MAX_DIRS: usize, + const MAX_FILES: usize, + const MAX_VOLUMES: usize, + > Seek for File<'_, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> +{ + fn seek(&mut self, pos: SeekFrom) -> Result { + match pos { + SeekFrom::Start(offset) => { + self.seek_from_start(offset.try_into().map_err(|_| Error::InvalidOffset)?)? + } + SeekFrom::End(offset) => { + self.seek_from_end((-offset).try_into().map_err(|_| Error::InvalidOffset)?)? + } + SeekFrom::Current(offset) => { + self.seek_from_current(offset.try_into().map_err(|_| Error::InvalidOffset)?)? + } + } + Ok(self.offset().into()) + } +} + #[cfg(feature = "defmt-log")] impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> defmt::Format for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> diff --git a/src/lib.rs b/src/lib.rs index f1c4e02..f510571 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -73,6 +73,8 @@ pub mod fat; pub mod filesystem; pub mod sdcard; +use core::fmt::Debug; +use embedded_io::ErrorKind; use filesystem::SearchId; #[doc(inline)] @@ -202,6 +204,40 @@ where DirAlreadyExists, } +impl embedded_io::Error for Error { + fn kind(&self) -> ErrorKind { + match self { + Error::DeviceError(_) + | Error::FormatError(_) + | Error::FileAlreadyOpen + | Error::DirAlreadyOpen + | Error::VolumeStillInUse + | Error::VolumeAlreadyOpen + | Error::EndOfFile + | Error::DiskFull + | Error::NotEnoughSpace + | Error::AllocationError => ErrorKind::Other, + Error::NoSuchVolume + | Error::FilenameError(_) + | Error::BadHandle + | Error::InvalidOffset => ErrorKind::InvalidInput, + Error::TooManyOpenVolumes | Error::TooManyOpenDirs | Error::TooManyOpenFiles => { + ErrorKind::OutOfMemory + } + Error::NotFound => ErrorKind::NotFound, + Error::OpenedDirAsFile + | Error::OpenedFileAsDir + | Error::DeleteDirAsFile + | Error::BadCluster + | Error::ConversionError + | Error::UnterminatedFatChain => ErrorKind::InvalidData, + Error::Unsupported | Error::BadBlockSize(_) => ErrorKind::Unsupported, + Error::ReadOnly => ErrorKind::PermissionDenied, + Error::FileAlreadyExists | Error::DirAlreadyExists => ErrorKind::AlreadyExists, + } + } +} + impl From for Error where E: core::fmt::Debug,