use serde::{Deserialize, Serialize};
use crate::raw::RawMetadata;
use std::path::Path;
use std::env;
#[allow(unused_imports)]
use pathdiff::diff_paths;
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct FpDirMetadata {
pub dirn: String,
pub basen: String,
pub relp: String,
pub reldirp: String,
pub ext: String,
pub fnnoext: String,
pub shortp: String,
pub filet: String,
pub sizeh: String,
}
impl FpDirMetadata {
pub fn from_raw(raw: &RawMetadata) -> Self {
let path = Path::new(&raw.pathname);
let current_dir = env::current_dir().unwrap_or_else(|_| Path::new(".").to_path_buf());
let dir_name = path.parent()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| "/".to_string());
let base_name = path.file_name()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or_default();
let rel_path = path.strip_prefix(¤t_dir)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|_| {
pathdiff::diff_paths(&path, ¤t_dir)
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| path.to_string_lossy().to_string())
});
let rel_dir_path = path.parent()
.and_then(|p| p.strip_prefix(¤t_dir).ok())
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| {
path.parent()
.and_then(|p| pathdiff::diff_paths(p, ¤t_dir))
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_else(|| {
path.parent()
.map(|p| p.to_string_lossy().to_string())
.unwrap_or_default()
})
});
let extension = path.extension()
.map(|e| e.to_string_lossy().to_string())
.unwrap_or_default();
let file_name_no_ext = path.file_stem()
.map(|n| n.to_string_lossy().to_string())
.unwrap_or(base_name.clone());
let short_path = if let Some(parent) = path.parent() {
if let Some(parent_name) = parent.file_name() {
format!("{}/{}", parent_name.to_string_lossy(), base_name)
} else {
base_name.clone()
}
} else {
base_name.clone()
};
let file_type = match raw.ftvalue.as_str() {
"S_IFDIR" => "directory".to_string(),
"S_IFREG" => "regular file".to_string(),
"S_IFLNK" => "symlink".to_string(),
"S_IFBLK" => "block device".to_string(),
"S_IFCHR" => "character device".to_string(),
"S_IFIFO" => "FIFO/pipe".to_string(),
"S_IFSOCK" => "socket".to_string(),
_ => "unknown".to_string(),
};
let size_human = format_size(raw.size);
FpDirMetadata {
dirn: dir_name,
basen: base_name,
relp: rel_path,
reldirp: rel_dir_path,
ext: extension,
fnnoext: file_name_no_ext,
shortp: short_path,
filet: file_type,
sizeh: size_human,
}
}
}
fn format_size(size: u64) -> String {
const UNITS: &[&str] = &["B", "KB", "MB", "GB", "TB"];
let mut size = size as f64;
let mut unit_index = 0;
while size >= 1024.0 && unit_index < UNITS.len() - 1 {
size /= 1024.0;
unit_index += 1;
}
if unit_index == 0 {
format!("{} {}", size as u64, UNITS[unit_index])
} else {
format!("{:.1} {}", size, UNITS[unit_index])
}
}