use core::convert::TryFrom;
use crate::blockdevice::BlockIdx;
use crate::fat::{FatType, OnDiskDirEntry};
use crate::filesystem::{Attributes, ClusterId, SearchId, ShortFileName, Timestamp};
use crate::{Error, RawVolume, VolumeManager};
use super::ToShortFileName;
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct DirEntry {
pub name: ShortFileName,
pub mtime: Timestamp,
pub ctime: Timestamp,
pub attributes: Attributes,
pub cluster: ClusterId,
pub size: u32,
pub entry_block: BlockIdx,
pub entry_offset: u32,
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct RawDirectory(pub(crate) SearchId);
impl RawDirectory {
pub fn to_directory<
D,
T,
const MAX_DIRS: usize,
const MAX_FILES: usize,
const MAX_VOLUMES: usize,
>(
self,
volume_mgr: &mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
) -> Directory<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
Directory::new(self, volume_mgr)
}
}
pub struct Directory<
'a,
D,
T,
const MAX_DIRS: usize,
const MAX_FILES: usize,
const MAX_VOLUMES: usize,
> where
D: crate::BlockDevice,
T: crate::TimeSource,
{
raw_directory: RawDirectory,
volume_mgr: &'a mut 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>
Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
pub fn new(
raw_directory: RawDirectory,
volume_mgr: &'a mut VolumeManager<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>,
) -> Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES> {
Directory {
raw_directory,
volume_mgr,
}
}
pub fn open_dir<N>(
&mut self,
name: N,
) -> Result<Directory<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, Error<D::Error>>
where
N: ToShortFileName,
{
let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
Ok(d.to_directory(self.volume_mgr))
}
pub fn change_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
where
N: ToShortFileName,
{
let d = self.volume_mgr.open_dir(self.raw_directory, name)?;
self.volume_mgr.close_dir(self.raw_directory).unwrap();
self.raw_directory = d;
Ok(())
}
pub fn find_directory_entry<N>(&mut self, name: N) -> Result<DirEntry, Error<D::Error>>
where
N: ToShortFileName,
{
self.volume_mgr
.find_directory_entry(self.raw_directory, name)
}
pub fn iterate_dir<F>(&mut self, func: F) -> Result<(), Error<D::Error>>
where
F: FnMut(&DirEntry),
{
self.volume_mgr.iterate_dir(self.raw_directory, func)
}
pub fn open_file_in_dir<N>(
&mut self,
name: N,
mode: crate::Mode,
) -> Result<crate::File<D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>, crate::Error<D::Error>>
where
N: super::ToShortFileName,
{
let f = self
.volume_mgr
.open_file_in_dir(self.raw_directory, name, mode)?;
Ok(f.to_file(self.volume_mgr))
}
pub fn delete_file_in_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
where
N: ToShortFileName,
{
self.volume_mgr.delete_file_in_dir(self.raw_directory, name)
}
pub fn make_dir_in_dir<N>(&mut self, name: N) -> Result<(), Error<D::Error>>
where
N: ToShortFileName,
{
self.volume_mgr.make_dir_in_dir(self.raw_directory, name)
}
pub fn to_raw_directory(self) -> RawDirectory {
let d = self.raw_directory;
core::mem::forget(self);
d
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize> Drop
for Directory<'a, D, T, MAX_DIRS, MAX_FILES, MAX_VOLUMES>
where
D: crate::BlockDevice,
T: crate::TimeSource,
{
fn drop(&mut self) {
self.volume_mgr
.close_dir(self.raw_directory)
.expect("Failed to close directory");
}
}
impl<'a, D, T, const MAX_DIRS: usize, const MAX_FILES: usize, const MAX_VOLUMES: usize>
core::fmt::Debug for Directory<'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, "Directory({})", self.raw_directory.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 Directory<'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, "Directory({})", self.raw_directory.0 .0)
}
}
#[cfg_attr(feature = "defmt-log", derive(defmt::Format))]
#[derive(Debug, Clone)]
pub(crate) struct DirectoryInfo {
pub(crate) directory_id: RawDirectory,
pub(crate) volume_id: RawVolume,
pub(crate) cluster: ClusterId,
}
impl DirEntry {
pub(crate) fn serialize(&self, fat_type: FatType) -> [u8; OnDiskDirEntry::LEN] {
let mut data = [0u8; OnDiskDirEntry::LEN];
data[0..11].copy_from_slice(&self.name.contents);
data[11] = self.attributes.0;
data[14..18].copy_from_slice(&self.ctime.serialize_to_fat()[..]);
let cluster_number = self.cluster.0;
let cluster_hi = if fat_type == FatType::Fat16 {
[0u8; 2]
} else {
u16::try_from((cluster_number >> 16) & 0x0000_FFFF)
.unwrap()
.to_le_bytes()
};
data[20..22].copy_from_slice(&cluster_hi[..]);
data[22..26].copy_from_slice(&self.mtime.serialize_to_fat()[..]);
let cluster_lo = u16::try_from(cluster_number & 0x0000_FFFF)
.unwrap()
.to_le_bytes();
data[26..28].copy_from_slice(&cluster_lo[..]);
data[28..32].copy_from_slice(&self.size.to_le_bytes()[..]);
data
}
pub(crate) fn new(
name: ShortFileName,
attributes: Attributes,
cluster: ClusterId,
ctime: Timestamp,
entry_block: BlockIdx,
entry_offset: u32,
) -> Self {
Self {
name,
mtime: ctime,
ctime,
attributes,
cluster,
size: 0,
entry_block,
entry_offset,
}
}
}