use super::{VfsError, VfsStat}; use chrono::Datelike; use std::os::unix::fs::PermissionsExt; use std::path::Path; /// 从 std::io::ErrorKind 映射 VfsError pub fn map_io_error(path: &Path, e: std::io::Error) -> VfsError { match e.kind() { std::io::ErrorKind::NotFound => VfsError::NotFound(path.display().to_string()), std::io::ErrorKind::PermissionDenied => { VfsError::PermissionDenied(path.display().to_string()) } std::io::ErrorKind::AlreadyExists => VfsError::AlreadyExists(path.display().to_string()), std::io::ErrorKind::DirectoryNotEmpty => VfsError::NotEmpty(path.display().to_string()), std::io::ErrorKind::NotADirectory => VfsError::NotADirectory(path.display().to_string()), std::io::ErrorKind::IsADirectory => VfsError::IsADirectory(path.display().to_string()), std::io::ErrorKind::UnexpectedEof => VfsError::UnexpectedEof, other => VfsError::Io(format!("{}: {}", other, path.display())), } } /// 从 std::fs::Metadata 构建 VfsStat pub fn stat_from_metadata(meta: &std::fs::Metadata, is_symlink: bool) -> VfsStat { #[cfg(unix)] use std::os::unix::fs::MetadataExt; let mut stat = VfsStat::new(); stat.size = meta.len(); stat.is_dir = meta.is_dir(); stat.is_symlink = is_symlink; #[cfg(unix)] { stat.mode = meta.permissions().mode(); stat.uid = meta.uid(); stat.gid = meta.gid(); } #[cfg(not(unix))] { stat.mode = if meta.is_dir() { 0o40755 } else { 0o100644 }; } if let Ok(t) = meta.accessed() { stat.atime = t; } if let Ok(t) = meta.modified() { stat.mtime = t; } stat } /// 构建目录条目的 long_name(类似 ls -l 格式) pub fn build_long_name(stat: &VfsStat, name: &str) -> String { let file_type = if stat.is_dir { 'd' } else { '-' }; let perms = format_permissions(stat.mode & 0o777); let link_count = if stat.is_dir { 3 } else { 1 }; let size = stat.size; let mtime = match stat.mtime.duration_since(std::time::UNIX_EPOCH) { Ok(d) => { let secs = d.as_secs(); format_timestamp(secs) } Err(_) => "Jan 1 1970".to_string(), }; format!( "{}{} {} {} {} {} {} {}", file_type, perms, link_count, stat.uid, stat.gid, size, mtime, name ) } fn format_permissions(mode: u32) -> String { let rwx = |n: u32| -> String { let r = if n & 4 != 0 { 'r' } else { '-' }; let w = if n & 2 != 0 { 'w' } else { '-' }; let x = if n & 1 != 0 { 'x' } else { '-' }; format!("{}{}{}", r, w, x) }; format!( "{}{}{}", rwx((mode >> 6) & 7), rwx((mode >> 3) & 7), rwx(mode & 7) ) } fn format_timestamp(secs: u64) -> String { let datetime = match chrono::DateTime::from_timestamp(secs as i64, 0) { Some(dt) => dt, None => return "Jan 1 1970".to_string(), }; let now = chrono::Utc::now(); if datetime.year() == now.year() { datetime.format("%b %e %H:%M").to_string() } else { datetime.format("%b %e %Y").to_string() } }