use super::*;
use core::ops;
use crate::*;
#[cfg(not(feature = "std"))]
use alloc::{borrow::ToOwned, boxed::Box, string::String};
use ::time;
use bincode::{Decode, Encode};
use embedded_io::*;
use time::{Date, PrimitiveDateTime};
#[derive(Debug, Clone, Copy)]
pub struct Attributes {
pub read_only: bool,
pub hidden: bool,
pub system: bool,
pub archive: bool,
}
impl From<RawAttributes> for Attributes {
fn from(value: RawAttributes) -> Self {
Attributes {
read_only: value.contains(RawAttributes::READ_ONLY),
hidden: value.contains(RawAttributes::HIDDEN),
system: value.contains(RawAttributes::SYSTEM),
archive: value.contains(RawAttributes::ARCHIVE),
}
}
}
pub(crate) const DIRENTRY_SIZE: usize = 32;
pub(crate) const SFN_NAME_LEN: usize = 8;
pub(crate) const SFN_EXT_LEN: usize = 3;
pub(crate) const SFN_LEN: usize = SFN_NAME_LEN + 1 + SFN_EXT_LEN;
#[derive(Encode, Decode, Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct Sfn {
pub(crate) name: [u8; SFN_NAME_LEN],
pub(crate) ext: [u8; SFN_EXT_LEN],
}
pub(crate) const CURRENT_DIR_SFN: Sfn = Sfn {
name: {
use typed_path::constants::windows::CURRENT_DIR;
let mut s = [b' '; SFN_NAME_LEN];
s[0] = CURRENT_DIR[0];
s
},
ext: [b' '; SFN_EXT_LEN],
};
pub(crate) const PARENT_DIR_SFN: Sfn = Sfn {
name: {
use typed_path::constants::windows::PARENT_DIR;
let mut s = [b' '; SFN_NAME_LEN];
s[0] = PARENT_DIR[0];
s[1] = PARENT_DIR[1];
s
},
ext: [b' '; SFN_EXT_LEN],
};
impl Sfn {
fn get_byte_slice(&self) -> [u8; SFN_NAME_LEN + SFN_EXT_LEN] {
let mut slice = [0; SFN_NAME_LEN + SFN_EXT_LEN];
slice[..SFN_NAME_LEN].copy_from_slice(&self.name);
slice[SFN_NAME_LEN..].copy_from_slice(&self.ext);
slice
}
pub(crate) fn gen_checksum(&self) -> u8 {
let mut sum = 0;
for c in self.get_byte_slice() {
sum = (if (sum & 1) != 0 { 0x80_u8 } else { 0_u8 })
.wrapping_add(sum >> 1)
.wrapping_add(c)
}
sum
}
pub(crate) fn decode(&self, codepage: &Codepage) -> String {
let mut string = String::with_capacity(SFN_LEN);
string.push_str(codepage.decode(&self.name).trim_end());
let ext = codepage.decode(&self.ext).trim_end().to_owned();
if !ext.is_empty() {
string.push_str(&ext);
};
string
}
}
#[derive(Clone, Debug)]
pub struct Properties {
pub(crate) path: Box<Path>,
pub(crate) sfn: (Sfn, Codepage),
pub(crate) is_dir: bool,
pub(crate) attributes: Attributes,
pub(crate) created: Option<PrimitiveDateTime>,
pub(crate) modified: PrimitiveDateTime,
pub(crate) accessed: Option<Date>,
pub(crate) file_size: u32,
pub(crate) data_cluster: u32,
pub(crate) chain: DirEntryChain,
}
impl Properties {
#[inline]
pub fn path(&self) -> &Path {
&self.path
}
#[inline]
pub fn sfn(&self) -> String {
self.sfn.0.decode(&self.sfn.1)
}
#[inline]
pub fn is_dir(&self) -> bool {
self.is_dir
}
#[inline]
pub fn is_file(&self) -> bool {
!self.is_dir()
}
#[inline]
pub fn attributes(&self) -> &Attributes {
&self.attributes
}
#[inline]
pub fn creation_time(&self) -> &Option<PrimitiveDateTime> {
&self.created
}
#[inline]
pub fn modification_time(&self) -> &PrimitiveDateTime {
&self.modified
}
#[inline]
pub fn last_accessed_date(&self) -> &Option<Date> {
&self.accessed
}
#[inline]
pub fn file_size(&self) -> u32 {
self.file_size
}
}
impl Properties {
pub(crate) fn from_raw(raw_props: RawProperties, path: Box<Path>, codepage: Codepage) -> Self {
Self {
path,
sfn: (raw_props.sfn, codepage),
is_dir: raw_props.is_dir,
attributes: raw_props.attributes.into(),
created: raw_props.created,
modified: raw_props.modified,
accessed: raw_props.accessed,
file_size: raw_props.file_size,
data_cluster: raw_props.data_cluster,
chain: raw_props.chain,
}
}
}
#[derive(Debug)]
pub struct DirEntry<'a, S>
where
S: Read + Seek,
{
pub(crate) entry: Properties,
pub(crate) fs: &'a FileSystem<S>,
}
impl<'a, S> DirEntry<'a, S>
where
S: Read + Seek,
{
pub fn to_ro_file(&self) -> Option<ROFile<'a, S>> {
self.is_file().then(|| ROFile {
fs: self.fs,
props: FileProps {
entry: self.entry.clone(),
offset: 0,
current_cluster: self.data_cluster,
},
})
}
pub fn to_dir(&self) -> Option<ReadDir<'a, S>> {
self.is_dir().then(|| {
ReadDir::new(
self.fs,
&EntryLocationUnit::DataCluster(self.data_cluster),
self.path(),
)
})
}
}
impl<'a, S> DirEntry<'a, S>
where
S: Read + Write + Seek,
{
pub fn to_rw_file(self) -> Option<RWFile<'a, S>> {
self.to_ro_file().map(|ro_file| ro_file.into())
}
}
impl<S> ops::Deref for DirEntry<'_, S>
where
S: Read + Seek,
{
type Target = Properties;
#[inline]
fn deref(&self) -> &Self::Target {
&self.entry
}
}
#[derive(Debug)]
pub struct ReadDir<'a, S>
where
S: Read + Seek,
{
inner: ReadDirInt<'a, S>,
parent: Box<Path>,
}
impl<'a, S> ReadDir<'a, S>
where
S: Read + Seek,
{
pub(crate) fn new<P>(fs: &'a FileSystem<S>, chain_start: &EntryLocationUnit, parent: P) -> Self
where
P: AsRef<Path>,
{
Self {
inner: ReadDirInt::new(fs, chain_start),
parent: parent.as_ref().into(),
}
}
}
impl<'a, S> Iterator for ReadDir<'a, S>
where
S: Read + Seek,
{
type Item = Result<DirEntry<'a, S>, S::Error>;
fn next(&mut self) -> Option<Self::Item> {
loop {
match self.inner.next() {
Some(res) => match res {
Ok(value) => {
if self.inner.fs.filter.borrow().filter(&value)
&& ![path_consts::CURRENT_DIR_STR, path_consts::PARENT_DIR_STR]
.contains(&value.name.as_str())
{
return Some(Ok(value.into_dir_entry(&self.parent, self.inner.fs)));
} else {
continue;
}
}
Err(err) => return Some(Err(err)),
},
None => return None,
}
}
}
}