use std::path::{Path, PathBuf};
use std::fs;
use std::io::{self, Read, Seek};
#[cfg(unix)]
use std::os::unix::fs::FileExt;
#[cfg(windows)]
use std::os::windows::fs::FileExt;
pub fn cleanup_db_directory(path: &Path) -> bool {
if path.exists() {
match fs::remove_dir_all(path) {
Ok(_) => true,
Err(e) => {
eprintln!("警告: 无法清理目录 {:?}: {}", path, e);
false
}
}
} else {
true
}
}
pub fn prepare_directory(path: &Path) -> bool {
if !cleanup_db_directory(path) {
return false;
}
match fs::create_dir_all(path) {
Ok(_) => true,
Err(e) => {
eprintln!("错误: 无法创建目录 {:?}: {}", path, e);
false
}
}
}
pub fn is_path_writable(path: &Path) -> bool {
if !path.exists() {
if let Err(_) = fs::create_dir_all(path) {
return false;
}
}
let test_file = path.join(".permission_test");
match fs::File::create(&test_file) {
Ok(file) => {
drop(file); let _ = fs::remove_file(test_file);
true
}
Err(_) => false
}
}
pub fn sync_directory(path: &Path) -> std::io::Result<()> {
#[cfg(unix)]
{
fs::File::open(path)?.sync_all()?;
}
#[cfg(windows)]
{
}
Ok(())
}
pub fn read_exact_at(file: &fs::File, mut buf: &mut [u8], offset: u64) -> std::io::Result<()> {
#[cfg(unix)]
{
file.read_exact_at(buf, offset)
}
#[cfg(windows)]
{
let bytes_read = file.seek_read(buf, offset)?;
if bytes_read != buf.len() {
return Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to read whole buffer",
));
}
Ok(())
}
#[cfg(not(any(unix, windows)))]
{
let mut file_clone = file.try_clone()?;
file_clone.seek(io::SeekFrom::Start(offset))?;
file_clone.read_exact(buf)
}
}
pub fn setup_example_db(example_name: &str) -> PathBuf {
use std::time::{SystemTime, UNIX_EPOCH};
use std::sync::atomic::{AtomicUsize, Ordering};
static COUNTER: AtomicUsize = AtomicUsize::new(0);
let timestamp = SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_micros();
let counter = COUNTER.fetch_add(1, Ordering::SeqCst);
let db_path = PathBuf::from(format!("{}_{}_{}_db", example_name, timestamp, counter));
if !prepare_directory(&db_path) {
panic!("无法准备示例数据库目录: {:?}", db_path);
}
db_path
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cleanup_db_directory() {
let path = PathBuf::from("test_cleanup");
fs::create_dir_all(&path).unwrap();
assert!(cleanup_db_directory(&path));
assert!(!path.exists());
}
#[test]
fn test_prepare_directory() {
let path = PathBuf::from("test_prepare");
assert!(prepare_directory(&path));
assert!(path.exists());
}
#[test]
fn test_is_path_writable() {
let path = PathBuf::from("test_writable");
assert!(is_path_writable(&path));
cleanup_db_directory(&path);
}
#[test]
fn test_setup_example_db() {
let path = setup_example_db("test_setup");
assert!(path.exists());
assert!(path.to_str().unwrap().starts_with("test_setup_"));
assert!(path.to_str().unwrap().ends_with("_db"));
cleanup_db_directory(&path);
}
}