use serde::{Deserialize, Serialize};
use crate::raw::RawMetadata;
use std::path::Path;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct PermsMetadata {
pub user: String,
pub group: String,
pub perms: String,
pub ownerp: String,
pub groupp: String,
pub othersp: String,
pub isfile: bool,
pub issyml: bool,
pub isdir: bool,
pub isexec: bool,
}
impl PermsMetadata {
pub fn from_raw(raw: &RawMetadata) -> Self {
let path = Path::new(&raw.pathname);
let is_symlink = path.is_symlink();
let (is_file, is_dir) = if is_symlink {
match raw.ftvalue.as_str() {
"S_IFREG" => (true, false), "S_IFDIR" => (false, true), _ => (false, false), }
} else {
(path.is_file(), path.is_dir())
};
let is_executable = if is_file {
#[cfg(unix)]
{
(raw.mode & 0o111) != 0
}
#[cfg(not(unix))]
{
false }
} else {
false
};
let perms_str = format_permissions(raw.mode);
let (owner_perms, group_perms, others_perms) = extract_permission_parts(raw.mode);
#[cfg(unix)]
let (user_name, group_name) = {
let uid = raw.uid;
let gid = raw.gid;
let user = get_user_name(uid).unwrap_or_else(|| uid.to_string());
let group = get_group_name(gid).unwrap_or_else(|| gid.to_string());
(user, group)
};
#[cfg(not(unix))]
let (user_name, group_name) = ("unknown".to_string(), "unknown".to_string());
PermsMetadata {
user: user_name,
group: group_name,
perms: perms_str,
ownerp: owner_perms,
groupp: group_perms,
othersp: others_perms,
isfile: is_file,
issyml: is_symlink,
isdir: is_dir,
isexec: is_executable,
}
}
}
fn format_permissions(mode: u32) -> String {
let mut result = String::with_capacity(10);
result.push(if (mode & 0o40000) != 0 {
'd' } else if (mode & 0o100000) != 0 {
'-' } else if (mode & 0o120000) != 0 {
'l' } else {
'?'
});
result.push(if (mode & 0o400) != 0 { 'r' } else { '-' });
result.push(if (mode & 0o200) != 0 { 'w' } else { '-' });
result.push(if (mode & 0o100) != 0 { 'x' } else { '-' });
result.push(if (mode & 0o40) != 0 { 'r' } else { '-' });
result.push(if (mode & 0o20) != 0 { 'w' } else { '-' });
result.push(if (mode & 0o10) != 0 { 'x' } else { '-' });
result.push(if (mode & 0o4) != 0 { 'r' } else { '-' });
result.push(if (mode & 0o2) != 0 { 'w' } else { '-' });
result.push(if (mode & 0o1) != 0 { 'x' } else { '-' });
result
}
fn extract_permission_parts(mode: u32) -> (String, String, String) {
let owner = format!("{}{}{}",
if (mode & 0o400) != 0 { 'r' } else { '-' },
if (mode & 0o200) != 0 { 'w' } else { '-' },
if (mode & 0o100) != 0 { 'x' } else { '-' }
);
let group = format!("{}{}{}",
if (mode & 0o40) != 0 { 'r' } else { '-' },
if (mode & 0o20) != 0 { 'w' } else { '-' },
if (mode & 0o10) != 0 { 'x' } else { '-' }
);
let others = format!("{}{}{}",
if (mode & 0o4) != 0 { 'r' } else { '-' },
if (mode & 0o2) != 0 { 'w' } else { '-' },
if (mode & 0o1) != 0 { 'x' } else { '-' }
);
(owner, group, others)
}
#[cfg(unix)]
fn get_user_name(uid: u32) -> Option<String> {
unsafe {
let passwd = libc::getpwuid(uid);
if passwd.is_null() {
None
} else {
let c_str = (*passwd).pw_name;
Some(std::ffi::CStr::from_ptr(c_str).to_string_lossy().into_owned())
}
}
}
#[cfg(unix)]
fn get_group_name(gid: u32) -> Option<String> {
unsafe {
let group = libc::getgrgid(gid);
if group.is_null() {
None
} else {
let c_str = (*group).gr_name;
Some(std::ffi::CStr::from_ptr(c_str).to_string_lossy().into_owned())
}
}
}