use crate::error::Result;
use anyhow::Context;
use std::fs;
use std::path::Path;
pub fn write_file<P: AsRef<Path>, D: AsRef<[u8]>>(path: P, data: D) -> Result<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).with_context(|| format!("创建父目录失败: {:?}", parent))?;
}
fs::write(path, data).with_context(|| format!("写入文件失败: {:?}", path))
}
pub fn read_file<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
let path = path.as_ref();
fs::read(path).with_context(|| format!("读取文件失败: {:?}", path))
}
#[cfg(feature = "mmap")]
pub mod mmap {
use crate::error::Result;
use anyhow::Context;
use memmap2::Mmap;
use std::fs;
use std::fs::File;
use std::path::Path;
pub unsafe fn map_file<P: AsRef<Path>>(path: P) -> Result<Mmap> {
let path = path.as_ref();
let file = File::open(path).with_context(|| format!("打开文件失败: {:?}", path))?;
let mmap = Mmap::map(&file).with_context(|| format!("内存映射失败: {:?}", path))?;
Ok(mmap)
}
pub unsafe fn map_file_mut<P: AsRef<Path>>(path: P) -> Result<memmap2::MmapMut> {
let path = path.as_ref();
let file = fs::OpenOptions::new()
.read(true)
.write(true)
.open(path)
.with_context(|| format!("打开文件失败: {:?}", path))?;
let mmap = memmap2::MmapMut::map_mut(&file)
.with_context(|| format!("内存映射失败: {:?}", path))?;
Ok(mmap)
}
}
#[cfg(feature = "async")]
pub mod async_io {
use crate::error::Result;
use anyhow::Context;
use std::path::Path;
use tokio::fs;
pub async fn write_file_async<P: AsRef<Path>, D: AsRef<[u8]>>(path: P, data: D) -> Result<()> {
let path = path.as_ref();
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
.await
.with_context(|| format!("创建父目录失败: {:?}", parent))?;
}
fs::write(path, data)
.await
.with_context(|| format!("写入文件失败: {:?}", path))
}
pub async fn read_file_async<P: AsRef<Path>>(path: P) -> Result<Vec<u8>> {
let path = path.as_ref();
fs::read(path)
.await
.with_context(|| format!("读取文件失败: {:?}", path))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn tmp_file(tag: &str) -> std::path::PathBuf {
std::env::temp_dir().join(format!(
"xtap_core_lib_io_test_{}_{}",
tag,
std::process::id()
))
}
#[test]
fn write_then_read_roundtrip() {
let p = tmp_file("roundtrip");
let data = b"hello xtap core lib";
write_file(&p, data).expect("写入应成功");
let read = read_file(&p).expect("读取应成功");
assert_eq!(read, data);
std::fs::remove_file(&p).ok();
}
#[test]
fn write_creates_parent_dirs() {
let p = tmp_file("nested").join("a/b/c/data.bin");
write_file(&p, vec![1u8, 2, 3]).expect("写入应自动创建父目录");
assert!(p.exists());
assert_eq!(read_file(&p).unwrap(), vec![1u8, 2, 3]);
let _ = std::fs::remove_dir_all(tmp_file("nested"));
}
#[test]
fn read_missing_file_errors() {
let p = tmp_file("missing");
let _ = std::fs::remove_file(&p);
assert!(read_file(&p).is_err(), "读取不存在的文件应返回 Err");
}
#[cfg(feature = "mmap")]
#[test]
fn mmap_roundtrip() {
let p = tmp_file("mmap");
write_file(&p, vec![7u8; 64]).unwrap();
let mmap = unsafe { super::mmap::map_file(&p) }.expect("mmap 读取应成功");
assert_eq!(mmap.len(), 64);
assert!(mmap.iter().all(|&b| b == 7));
std::fs::remove_file(&p).ok();
}
}