#![cfg_attr(not(test), no_std)]
#![deny(missing_docs)]
#[cfg(test)]
#[macro_use]
extern crate hex_literal;
#[macro_use]
mod structure;
pub mod blockdevice;
pub mod fat;
pub mod filesystem;
pub mod sdcard;
use core::fmt::Debug;
use embedded_io::ErrorKind;
use filesystem::Handle;
#[doc(inline)]
pub use crate::blockdevice::{Block, BlockCache, BlockCount, BlockDevice, BlockIdx};
#[doc(inline)]
pub use crate::fat::{FatVolume, VolumeName};
#[doc(inline)]
pub use crate::filesystem::{
Attributes, ClusterId, DirEntry, Directory, File, FilenameError, LfnBuffer, Mode, RawDirectory,
RawFile, ShortFileName, TimeSource, Timestamp, MAX_FILE_SIZE,
};
use filesystem::DirectoryInfo;
#[doc(inline)]
pub use crate::sdcard::Error as SdCardError;
#[doc(inline)]
pub use crate::sdcard::SdCard;
mod volume_mgr;
#[doc(inline)]
pub use volume_mgr::VolumeManager;
#[cfg(all(feature = "defmt-log", feature = "log"))]
compile_error!("Cannot enable both log and defmt-log");
#[cfg(feature = "log")]
use log::{debug, trace, warn};
#[cfg(feature = "defmt-log")]
use defmt::{debug, trace, warn};
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
#[macro_export]
macro_rules! debug {
($($arg:tt)+) => {};
}
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
#[macro_export]
macro_rules! trace {
($($arg:tt)+) => {};
}
#[cfg(all(not(feature = "defmt-log"), not(feature = "log")))]
#[macro_export]
macro_rules! warn {
($($arg:tt)+) => {};
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Clone)]
pub enum Error<E>
where
E: core::fmt::Debug,
{
DeviceError(E),
FormatError(&'static str),
NoSuchVolume,
FilenameError(FilenameError),
TooManyOpenVolumes,
TooManyOpenDirs,
TooManyOpenFiles,
BadHandle,
NotFound,
FileAlreadyOpen,
DirAlreadyOpen,
OpenedDirAsFile,
OpenedFileAsDir,
DeleteDirAsFile,
VolumeStillInUse,
VolumeAlreadyOpen,
Unsupported,
EndOfFile,
BadCluster,
ConversionError,
NotEnoughSpace,
AllocationError,
UnterminatedFatChain,
ReadOnly,
FileAlreadyExists,
BadBlockSize(u16),
InvalidOffset,
DiskFull,
DirAlreadyExists,
LockError,
}
impl<E: Debug> embedded_io::Error for Error<E> {
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
| Error::LockError => 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<E> From<E> for Error<E>
where
E: core::fmt::Debug,
{
fn from(value: E) -> Error<E> {
Error::DeviceError(value)
}
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RawVolume(Handle);
impl RawVolume {
pub fn to_volume<
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>,
) -> Volume<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
Volume::new(self, volume_mgr)
}
}
pub struct Volume<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
raw_volume: RawVolume,
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>
Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
pub fn new(
raw_volume: RawVolume,
volume_mgr: &'a VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
) -> Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
Volume {
raw_volume,
volume_mgr,
}
}
pub fn open_root_dir(
&self,
) -> Result<crate::Directory<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>> {
let d = self.volume_mgr.open_root_dir(self.raw_volume)?;
Ok(d.to_directory(self.volume_mgr))
}
pub fn to_raw_volume(self) -> RawVolume {
let v = self.raw_volume;
core::mem::forget(self);
v
}
pub fn close(self) -> Result<(), Error<D::Error>> {
let result = self.volume_mgr.close_volume(self.raw_volume);
core::mem::forget(self);
result
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
for Volume<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
fn drop(&mut self) {
_ = self.volume_mgr.close_volume(self.raw_volume)
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
core::fmt::Debug for Volume<'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, "Volume({})", self.raw_volume.0 .0)
}
}
#[cfg(feature = "defmt-log")]
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
defmt::Format for Volume<'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, "Volume({})", self.raw_volume.0 .0)
}
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct VolumeInfo {
raw_volume: RawVolume,
idx: VolumeIdx,
volume_type: VolumeType,
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq)]
pub enum VolumeType {
Fat(FatVolume),
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub struct VolumeIdx(pub usize);
const PARTITION_ID_FAT32_LBA: u8 = 0x0C;
const PARTITION_ID_FAT16_LBA: u8 = 0x0E;
const PARTITION_ID_FAT16: u8 = 0x06;
const PARTITION_ID_FAT16_SMALL: u8 = 0x04;
const PARTITION_ID_FAT32_CHS_LBA: u8 = 0x0B;