use wtf_string::{Wtf16Str, Wtf16String};
use crate::WindowsFileTimestamp;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum EntryType {
File,
Directory,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FileIdentity {
id: [u8; 16],
volume_serial: Option<u64>,
}
impl FileIdentity {
#[must_use]
pub const fn new(id: [u8; 16], volume_serial: Option<u64>) -> Self {
Self { id, volume_serial }
}
#[must_use]
pub const fn id_bytes(&self) -> [u8; 16] {
self.id
}
#[must_use]
pub const fn volume_serial(&self) -> Option<u64> {
self.volume_serial
}
#[must_use]
pub const fn is_volume_qualified(&self) -> bool {
self.volume_serial.is_some()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum FileIdentityMode {
#[default]
Omit,
BestEffort,
Required,
}
impl FileIdentityMode {
#[must_use]
pub const fn queries_volume(self) -> bool {
matches!(
self,
FileIdentityMode::BestEffort | FileIdentityMode::Required
)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct DirectoryEntry {
name: Wtf16String,
attributes: u32,
reparse_tag: Option<u32>,
logical_size: u64,
allocation_size: u64,
extended_attribute_size: u32,
creation_time: WindowsFileTimestamp,
last_access_time: WindowsFileTimestamp,
last_write_time: WindowsFileTimestamp,
change_time: WindowsFileTimestamp,
identity: FileIdentity,
}
pub(crate) struct EntryFields {
pub(crate) name: Wtf16String,
pub(crate) attributes: u32,
pub(crate) logical_size: u64,
pub(crate) allocation_size: u64,
pub(crate) extended_attribute_size: u32,
pub(crate) creation_time: WindowsFileTimestamp,
pub(crate) last_access_time: WindowsFileTimestamp,
pub(crate) last_write_time: WindowsFileTimestamp,
pub(crate) change_time: WindowsFileTimestamp,
pub(crate) reparse_tag: u32,
pub(crate) identity: FileIdentity,
}
const FILE_ATTRIBUTE_DIRECTORY: u32 = 0x0000_0010;
const FILE_ATTRIBUTE_REPARSE_POINT: u32 = 0x0000_0400;
impl DirectoryEntry {
#[must_use]
pub(crate) fn from_fields(fields: EntryFields) -> Self {
let reparse_tag =
(fields.attributes & FILE_ATTRIBUTE_REPARSE_POINT != 0).then_some(fields.reparse_tag);
Self {
name: fields.name,
attributes: fields.attributes,
reparse_tag,
logical_size: fields.logical_size,
allocation_size: fields.allocation_size,
extended_attribute_size: fields.extended_attribute_size,
creation_time: fields.creation_time,
last_access_time: fields.last_access_time,
last_write_time: fields.last_write_time,
change_time: fields.change_time,
identity: fields.identity,
}
}
#[must_use]
pub fn name(&self) -> &Wtf16Str {
&self.name
}
#[must_use]
pub fn into_name(self) -> Wtf16String {
self.name
}
#[must_use]
pub const fn entry_type(&self) -> EntryType {
if self.attributes & FILE_ATTRIBUTE_DIRECTORY != 0 {
EntryType::Directory
} else {
EntryType::File
}
}
#[must_use]
pub const fn attributes(&self) -> u32 {
self.attributes
}
#[must_use]
pub const fn is_reparse_point(&self) -> bool {
self.reparse_tag.is_some()
}
#[must_use]
pub const fn reparse_tag(&self) -> Option<u32> {
self.reparse_tag
}
#[must_use]
pub const fn logical_size(&self) -> u64 {
self.logical_size
}
#[must_use]
pub const fn allocation_size(&self) -> u64 {
self.allocation_size
}
#[must_use]
pub const fn extended_attribute_size(&self) -> u32 {
self.extended_attribute_size
}
#[must_use]
pub const fn creation_time(&self) -> WindowsFileTimestamp {
self.creation_time
}
#[must_use]
pub const fn last_access_time(&self) -> WindowsFileTimestamp {
self.last_access_time
}
#[must_use]
pub const fn last_write_time(&self) -> WindowsFileTimestamp {
self.last_write_time
}
#[must_use]
pub const fn change_time(&self) -> WindowsFileTimestamp {
self.change_time
}
#[must_use]
pub const fn identity(&self) -> FileIdentity {
self.identity
}
}
#[cfg(test)]
mod tests;