use super::{lowlevel::FileAttrs, UnixTimeStamp};
pub use super::lowlevel::{FileType as RawFileType, Permissions as RawPermissions};
#[derive(Debug, Default, Copy, Clone)]
pub struct MetaDataBuilder(FileAttrs);
impl MetaDataBuilder {
pub const fn new() -> Self {
Self(FileAttrs::new())
}
pub fn reset(&mut self) -> &mut Self {
self.0 = FileAttrs::new();
self
}
pub fn id(&mut self, (uid, gid): (u32, u32)) -> &mut Self {
self.0.set_id(uid, gid);
self
}
pub fn permissions(&mut self, perm: Permissions) -> &mut Self {
self.0.set_permissions(perm.0);
self
}
pub fn len(&mut self, len: u64) -> &mut Self {
self.0.set_size(len);
self
}
pub fn time(&mut self, accessed: UnixTimeStamp, modified: UnixTimeStamp) -> &mut Self {
self.0.set_time(accessed.0, modified.0);
self
}
pub fn create(&self) -> MetaData {
MetaData::new(self.0)
}
}
#[repr(transparent)]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
pub struct MetaData(FileAttrs);
#[allow(clippy::len_without_is_empty)]
impl MetaData {
pub(super) fn new(attrs: FileAttrs) -> Self {
Self(attrs)
}
pub(super) fn into_inner(self) -> FileAttrs {
self.0
}
pub fn len(&self) -> Option<u64> {
self.0.get_size()
}
pub fn uid(&self) -> Option<u32> {
self.0.get_id().map(|(uid, _gid)| uid)
}
pub fn gid(&self) -> Option<u32> {
self.0.get_id().map(|(_uid, gid)| gid)
}
pub fn permissions(&self) -> Option<Permissions> {
self.0.get_permissions().map(Permissions)
}
pub fn file_type(&self) -> Option<FileType> {
self.0.get_filetype().map(FileType)
}
pub fn accessed(&self) -> Option<UnixTimeStamp> {
self.0
.get_time()
.map(|(atime, _mtime)| atime)
.map(UnixTimeStamp)
}
pub fn modified(&self) -> Option<UnixTimeStamp> {
self.0
.get_time()
.map(|(_atime, mtime)| mtime)
.map(UnixTimeStamp)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct FileType(RawFileType);
impl FileType {
pub fn as_raw(&self) -> RawFileType {
self.0
}
pub fn is_dir(&self) -> bool {
self.0 == RawFileType::Directory
}
pub fn is_file(&self) -> bool {
self.0 == RawFileType::RegularFile
}
pub fn is_symlink(&self) -> bool {
self.0 == RawFileType::Symlink
}
pub fn is_fifo(&self) -> bool {
self.0 == RawFileType::FIFO
}
pub fn is_socket(&self) -> bool {
self.0 == RawFileType::Socket
}
pub fn is_block_device(&self) -> bool {
self.0 == RawFileType::BlockDevice
}
pub fn is_char_device(&self) -> bool {
self.0 == RawFileType::CharacterDevice
}
}
impl From<RawFileType> for FileType {
fn from(value: RawFileType) -> Self {
Self(value)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub struct Permissions(RawPermissions);
macro_rules! impl_getter_setter {
($getter_name:ident, $setter_name:ident, $variant:ident, $variant_name:expr) => {
#[doc = "Tests whether "]
#[doc = $variant_name]
#[doc = " bit is set."]
pub fn $getter_name(&self) -> bool {
self.0.intersects(RawPermissions::$variant)
}
#[doc = "Modify the "]
#[doc = $variant_name]
#[doc = " bit."]
pub fn $setter_name(&mut self, value: bool) -> &mut Self {
self.0.set(RawPermissions::$variant, value);
self
}
};
}
impl Permissions {
pub const fn new() -> Self {
Self(RawPermissions::empty())
}
impl_getter_setter!(suid, set_suid, SET_UID, "set-user-id");
impl_getter_setter!(sgid, set_sgid, SET_GID, "set-group-id");
impl_getter_setter!(svtx, set_vtx, SET_VTX, "set-sticky-bit");
impl_getter_setter!(
read_by_owner,
set_read_by_owner,
READ_BY_OWNER,
"read by owner"
);
impl_getter_setter!(
write_by_owner,
set_write_by_owner,
WRITE_BY_OWNER,
"write by owner"
);
impl_getter_setter!(
execute_by_owner,
set_execute_by_owner,
EXECUTE_BY_OWNER,
"execute by owner"
);
impl_getter_setter!(
read_by_group,
set_read_by_group,
READ_BY_GROUP,
"read by group"
);
impl_getter_setter!(
write_by_group,
set_write_by_group,
WRITE_BY_GROUP,
"write by group"
);
impl_getter_setter!(
execute_by_group,
set_execute_by_group,
EXECUTE_BY_GROUP,
"execute by group"
);
impl_getter_setter!(
read_by_other,
set_read_by_other,
READ_BY_OTHER,
"read by other"
);
impl_getter_setter!(
write_by_other,
set_write_by_other,
WRITE_BY_OTHER,
"write by other"
);
impl_getter_setter!(
execute_by_other,
set_execute_by_other,
EXECUTE_BY_OTHER,
"execute by other"
);
pub fn readonly(&self) -> bool {
!self.write_by_owner() && !self.write_by_group() && !self.write_by_other()
}
pub fn set_readonly(&mut self, readonly: bool) {
let writable = !readonly;
self.set_write_by_owner(writable);
self.set_write_by_group(writable);
self.set_write_by_other(writable);
}
pub fn as_raw(&self) -> RawPermissions {
self.0
}
pub fn from_raw(p: RawPermissions) -> Self {
Self(p)
}
}
impl From<u16> for Permissions {
fn from(octet: u16) -> Self {
Self(RawPermissions::from_bits((octet & 0o7777) as u32).unwrap())
}
}
impl Default for Permissions {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::Permissions;
#[test]
fn permission_from_u16() {
let p = Permissions::from(0o777);
assert!(p.execute_by_owner());
assert!(p.execute_by_group());
assert!(p.execute_by_other());
assert!(p.read_by_owner());
assert!(p.read_by_group());
assert!(p.read_by_other());
assert!(p.write_by_owner());
assert!(p.write_by_group());
assert!(p.write_by_other());
assert!(!p.svtx());
assert!(!p.sgid());
assert!(!p.suid());
assert_eq!(p.as_raw().bits(), 0o777);
let p = Permissions::from(0o400);
assert!(p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o400);
let p = Permissions::from(0o200);
assert!(!p.read_by_owner());
assert!(p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o200);
let p = Permissions::from(0o100);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o100);
let p = Permissions::from(0o40);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o40);
let p = Permissions::from(0o20);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o20);
let p = Permissions::from(0o10);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o10);
let p = Permissions::from(0o4);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o4);
let p = Permissions::from(0o2);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o2);
let p = Permissions::from(0o1);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o1);
let p = Permissions::from(0o4000);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(p.suid());
assert!(!p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o4000);
let p = Permissions::from(0o2000);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(p.sgid());
assert!(!p.svtx());
assert_eq!(p.as_raw().bits(), 0o2000);
let p = Permissions::from(0o1000);
assert!(!p.read_by_owner());
assert!(!p.write_by_owner());
assert!(!p.execute_by_owner());
assert!(!p.read_by_group());
assert!(!p.write_by_group());
assert!(!p.execute_by_group());
assert!(!p.read_by_other());
assert!(!p.write_by_other());
assert!(!p.execute_by_other());
assert!(!p.suid());
assert!(!p.sgid());
assert!(p.svtx());
assert_eq!(p.as_raw().bits(), 0o1000);
assert_eq!(Permissions::from(0o177777), Permissions::from(0o7777),);
}
}