use std::cell::RefCell;
use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;
use std::path::{Component, PathBuf};
use std::rc::Rc;
use users::{get_user_by_uid, get_group_by_gid};
use crate::bottle_cap::BottleCap;
use crate::file_list::FileBlocks;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct FileAtlas {
pub path: PathBuf, pub normalized_path: PathBuf, pub is_folder: bool,
pub size: u64,
pub perms: u32,
pub mtime_ns: i64,
pub ctime_ns: i64,
pub user: String,
pub group: String,
pub symlink_target: Option<PathBuf>,
pub contents: FileBlocks,
pub bottle_cap: BottleCap,
}
impl FileAtlas {
pub fn bobble(self) -> FileAtlasRef {
Rc::new(RefCell::new(self))
}
pub fn fix_bad_path(&mut self) -> Option<PathBuf> {
let mut components = self.normalized_path.components();
if !components.all(|c| matches!(c, Component::Normal(_))) {
let bad_path = self.normalized_path.clone();
self.normalized_path = components.filter(|c| matches!(c, Component::Normal(_))).collect();
Some(bad_path)
} else {
None
}
}
}
impl TryFrom<&Metadata> for FileAtlas {
type Error = std::io::Error;
fn try_from(metadata: &Metadata) -> std::io::Result<FileAtlas> {
let user = get_user_by_uid(metadata.uid());
let group = get_group_by_gid(metadata.gid());
Ok(FileAtlas {
path: PathBuf::default(),
normalized_path: PathBuf::default(),
is_folder: metadata.is_dir(),
size: if metadata.is_dir() { 0 } else { metadata.len() },
perms: metadata.mode(),
mtime_ns: metadata.mtime() * 1_000_000_000 + metadata.mtime_nsec(),
ctime_ns: metadata.ctime() * 1_000_000_000 + metadata.ctime_nsec(),
user: user.and_then(|u| u.name().to_str().map(|s| s.to_string())).unwrap_or_else(|| "?".to_string()),
group: group.and_then(|g| g.name().to_str().map(|s| s.to_string())).unwrap_or_else(|| "?".to_string()),
symlink_target: None,
contents: FileBlocks::default(),
bottle_cap: BottleCap::default(),
})
}
}
pub type FileAtlasRef = Rc<RefCell<FileAtlas>>;