use anyhow::{Context, Result};
use std::fs;
use std::path::{Path, PathBuf};
#[cfg(unix)]
use std::os::unix::fs::PermissionsExt;
pub fn check_file_readonly(path: &Path) -> Result<bool> {
if !path.exists() {
return Ok(false);
}
let metadata = fs::metadata(path).context("读取文件元数据失败")?;
#[cfg(unix)]
{
let perms = metadata.permissions();
let mode = perms.mode();
let is_readonly = (mode & 0o200) == 0;
Ok(is_readonly)
}
#[cfg(not(unix))]
{
let readonly = metadata.permissions().readonly();
Ok(readonly)
}
}
pub fn resolve_symlink(path: &Path) -> Result<PathBuf> {
if !path.exists() {
return Ok(path.to_path_buf());
}
#[cfg(unix)]
{
if let Ok(metadata) = fs::metadata(path) {
let file_type = metadata.file_type();
if file_type.is_symlink() {
return fs::canonicalize(path).context("解析符号链接失败");
}
}
}
Ok(path.to_path_buf())
}
pub fn check_disk_space(path: &Path, required_bytes: u64) -> Result<()> {
#[cfg(unix)]
{
if let Ok(stat) = fs2::statvfs(path) {
let available = stat.available_space();
if available < required_bytes {
anyhow::bail!(
"磁盘空间不足。需要 {} 字节,可用 {} 字节",
required_bytes,
available
);
}
}
}
#[cfg(not(unix))]
{
}
Ok(())
}
pub fn atomic_write(path: &Path, content: &[u8]) -> Result<()> {
let temp_path = path.with_extension("tmp");
fs::write(&temp_path, content).context("写入临时文件失败")?;
fs::rename(&temp_path, path).context("移动文件失败")?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_atomic_write() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test.txt");
let content = b"Hello, World!";
atomic_write(&file_path, content).unwrap();
assert_eq!(fs::read(&file_path).unwrap(), content);
}
}