use super::TimeSource;
use crate::{
filesystem::{ClusterId, DirEntry, Handle},
BlockDevice, Error, RawVolume, VolumeManager,
};
use embedded_io::{ErrorType, Read, Seek, SeekFrom, Write};
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RawFile(pub(crate) Handle);
impl RawFile {
pub fn to_file<D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>(
self,
volume_mgr: &VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
) -> File<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
File::new(self, volume_mgr)
}
}
pub struct File<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
raw_file: RawFile,
volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
pub fn new(
raw_file: RawFile,
volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
) -> File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
File {
raw_file,
volume_mgr,
}
}
pub fn read(&self, buffer: &mut [u8]) -> Result<usize, crate::Error<D::Error>> {
self.volume_mgr.read(self.raw_file, buffer)
}
pub fn write(&self, buffer: &[u8]) -> Result<(), crate::Error<D::Error>> {
self.volume_mgr.write(self.raw_file, buffer)
}
pub fn is_eof(&self) -> bool {
self.volume_mgr
.file_eof(self.raw_file)
.expect("Corrupt file ID")
}
pub fn seek_from_current(&self, offset: i32) -> Result<(), crate::Error<D::Error>> {
self.volume_mgr
.file_seek_from_current(self.raw_file, offset)
}
pub fn seek_from_start(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
self.volume_mgr.file_seek_from_start(self.raw_file, offset)
}
pub fn seek_from_end(&self, offset: u32) -> Result<(), crate::Error<D::Error>> {
self.volume_mgr.file_seek_from_end(self.raw_file, offset)
}
pub fn length(&self) -> u32 {
self.volume_mgr
.file_length(self.raw_file)
.expect("Corrupt file ID")
}
pub fn offset(&self) -> u32 {
self.volume_mgr
.file_offset(self.raw_file)
.expect("Corrupt file ID")
}
pub fn to_raw_file(self) -> RawFile {
let f = self.raw_file;
core::mem::forget(self);
f
}
pub fn flush(&self) -> Result<(), Error<D::Error>> {
self.volume_mgr.flush_file(self.raw_file)
}
pub fn close(self) -> Result<(), Error<D::Error>> {
let result = self.volume_mgr.close_file(self.raw_file);
core::mem::forget(self);
result
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
fn drop(&mut self) {
_ = self.volume_mgr.close_file(self.raw_file);
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
core::fmt::Debug for File<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "File({})", self.raw_file.0 .0)
}
}
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<D::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<usize, Self::Error> {
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<usize, Self::Error> {
if buf.is_empty() {
Ok(0)
} else {
self.write(buf)?;
Ok(buf.len())
}
}
fn flush(&mut self) -> Result<(), Self::Error> {
Self::flush(self)
}
}
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<u64, Self::Error> {
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>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
fn format(&self, fmt: defmt::Formatter) {
defmt::write!(fmt, "File({})", self.raw_file.0 .0)
}
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FileError {
InvalidOffset,
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum Mode {
ReadOnly,
ReadWriteAppend,
ReadWriteTruncate,
ReadWriteCreate,
ReadWriteCreateOrTruncate,
ReadWriteCreateOrAppend,
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Clone)]
pub(crate) struct FileInfo {
pub(crate) raw_file: RawFile,
pub(crate) raw_volume: RawVolume,
pub(crate) current_cluster: (u32, ClusterId),
pub(crate) current_offset: u32,
pub(crate) mode: Mode,
pub(crate) entry: DirEntry,
pub(crate) dirty: bool,
}
impl FileInfo {
pub fn eof(&self) -> bool {
self.current_offset == self.entry.size
}
pub fn length(&self) -> u32 {
self.entry.size
}
pub fn seek_from_start(&mut self, offset: u32) -> Result<(), FileError> {
if offset > self.entry.size {
return Err(FileError::InvalidOffset);
}
self.current_offset = offset;
Ok(())
}
pub fn seek_from_end(&mut self, offset: u32) -> Result<(), FileError> {
if offset > self.entry.size {
return Err(FileError::InvalidOffset);
}
self.current_offset = self.entry.size - offset;
Ok(())
}
pub fn seek_from_current(&mut self, offset: i32) -> Result<(), FileError> {
let new_offset = i64::from(self.current_offset) + i64::from(offset);
if new_offset < 0 || new_offset > i64::from(self.entry.size) {
return Err(FileError::InvalidOffset);
}
self.current_offset = new_offset as u32;
Ok(())
}
pub fn left(&self) -> u32 {
self.entry.size - self.current_offset
}
pub(crate) fn update_length(&mut self, new: u32) {
self.entry.size = new;
}
}