use alloc::format;
use alloc::string::String;
use core::fmt;
use core::fmt::{Debug, Formatter};
use crate::api::Metadata;
use crate::api::raw_directory_entry::{Attributes, VfatDirectoryEntry};
use crate::api::timestamp::{Milliseconds, VfatTimestamp};
use crate::{ClusterId, const_assert_size};
#[derive(Copy, Clone)]
#[repr(C, packed)]
pub struct RegularDirectoryEntry {
pub file_name: [u8; 8],
pub file_ext: [u8; 3],
pub(crate) attributes: Attributes,
pub(crate) _reseverd_win_nt: u8,
pub(crate) creation_millis: Milliseconds,
pub creation_time: VfatTimestamp,
pub last_access_date: u16,
pub high_16bits: u16,
pub last_modification_time: VfatTimestamp,
pub low_16bits: u16,
pub file_size: u32,
}
const_assert_size!(RegularDirectoryEntry, 32);
impl Debug for RegularDirectoryEntry {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("RegularDirectoryEntry")
.field(
"file_name",
&format_args!("{}", String::from_utf8_lossy(&{ self.file_name })),
)
.field(
"file_ext",
&format_args!("{}", String::from_utf8_lossy(&{ self.file_ext })),
)
.field("attributes", &{ self.attributes })
.field("file_size", &{ self.file_size })
.field("low-high", &format_args!("{:?}", self.cluster()))
.finish()
}
}
impl From<Metadata> for RegularDirectoryEntry {
fn from(metadata: Metadata) -> Self {
let file_name = VfatDirectoryEntry::regular_filename_from(metadata.name());
let file_ext = VfatDirectoryEntry::get_regular_filename_ext(metadata.name());
let (high_16bits, low_16bits) = metadata.cluster.into_high_low();
RegularDirectoryEntry {
file_name,
file_ext,
high_16bits,
low_16bits,
attributes: metadata.attributes,
creation_time: metadata.creation().unwrap(),
last_modification_time: metadata.last_update().unwrap(),
file_size: metadata.size,
_reseverd_win_nt: 0,
creation_millis: Default::default(),
last_access_date: 0,
}
}
}
impl RegularDirectoryEntry {
pub fn is_dir(&self) -> bool {
self.attributes.is_directory()
}
pub(crate) fn cluster(&self) -> ClusterId {
ClusterId::from_high_low(self.high_16bits, self.low_16bits)
}
pub fn is_volume_id(&self) -> bool {
self.attributes.is_volume_id()
}
pub fn is_lfn(&self) -> bool {
self.attributes.is_lfn()
}
pub fn full_name(&self) -> String {
let name = String::from_utf8_lossy(self.file_name());
let ext = self
.extension()
.map(|ext| format!(".{}", String::from_utf8_lossy(ext)))
.unwrap_or_default();
format!("{}{}", name, ext)
}
fn early_terminate_pos(v: &[u8]) -> usize {
for (pos, ch) in v.iter().enumerate() {
if *ch == 0x00 || *ch == 0x20 {
return pos;
}
}
v.len()
}
pub fn file_name(&self) -> &[u8] {
let pos = Self::early_terminate_pos(&self.file_name);
&self.file_name[..pos]
}
pub fn extension(&self) -> Option<&[u8]> {
let pos = Self::early_terminate_pos(&self.file_ext);
(pos > 0).then(|| &self.file_ext[..pos])
}
}