use anyhow::Result;
use std::path::Path;
pub fn read_to_string(path: &str) -> Result<String> {
Ok(std::fs::read_to_string(path)?)
}
pub fn write(path: &str, contents: &str) -> Result<()> {
Ok(std::fs::write(path, contents)?)
}
pub fn read(path: &str) -> Result<Vec<u8>> {
Ok(std::fs::read(path)?)
}
pub fn create_dir(path: &str) -> Result<()> {
Ok(std::fs::create_dir(path)?)
}
pub fn create_dir_all(path: &str) -> Result<()> {
Ok(std::fs::create_dir_all(path)?)
}
pub fn remove_file(path: &str) -> Result<()> {
Ok(std::fs::remove_file(path)?)
}
pub fn remove_dir(path: &str) -> Result<()> {
Ok(std::fs::remove_dir(path)?)
}
pub fn copy(from: &str, to: &str) -> Result<u64> {
Ok(std::fs::copy(from, to)?)
}
pub fn rename(from: &str, to: &str) -> Result<()> {
Ok(std::fs::rename(from, to)?)
}
pub fn read_dir(path: &str) -> Result<Vec<std::fs::DirEntry>> {
let entries: Result<Vec<_>, _> = std::fs::read_dir(path)?.collect();
Ok(entries?)
}
pub fn metadata(path: &str) -> Result<std::fs::Metadata> {
Ok(std::fs::metadata(path)?)
}
pub fn exists(path: &str) -> bool {
Path::new(path).exists()
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn test_write_and_read_round_trip() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("test.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
let content = "Hello, Ruchy!";
write(path_str, content).expect("write should succeed in test");
let read_content = read_to_string(path_str).expect("read_to_string should succeed in test");
assert_eq!(content, read_content, "Round-trip should preserve content");
}
#[test]
fn test_write_empty_string() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("empty.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
write(path_str, "").expect("write should succeed in test");
let content = read_to_string(path_str).expect("read_to_string should succeed in test");
assert_eq!(content, "", "Empty file should read as empty string");
}
#[test]
fn test_write_multiline_content() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("multiline.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
let content = "Line 1\nLine 2\nLine 3";
write(path_str, content).expect("write should succeed in test");
let read_content = read_to_string(path_str).expect("read_to_string should succeed in test");
assert_eq!(content, read_content);
}
#[test]
fn test_write_unicode() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("unicode.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
let content = "Hello 世界 🦀";
write(path_str, content).expect("write should succeed in test");
let read_content = read_to_string(path_str).expect("read_to_string should succeed in test");
assert_eq!(content, read_content, "Unicode should round-trip");
}
#[test]
fn test_read_nonexistent_file() {
let result = read_to_string("/nonexistent/file.txt");
assert!(result.is_err(), "Reading nonexistent file should fail");
}
#[test]
fn test_write_to_invalid_path() {
let result = write("/invalid/\0/path.txt", "content");
assert!(result.is_err(), "Writing to invalid path should fail");
}
#[test]
fn test_read_binary_data() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("binary.dat");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
let binary_data = vec![0u8, 1, 2, 255, 128];
std::fs::write(&file_path, &binary_data).expect("std::fs::write should succeed in test");
let read_data = read(path_str).expect("read should succeed in test");
assert_eq!(binary_data, read_data, "Binary data should round-trip");
}
#[test]
fn test_create_and_remove_dir() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dir_path = temp_dir.path().join("new_dir");
let path_str = dir_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir(path_str).expect("create_dir should succeed in test");
assert!(exists(path_str), "Directory should exist after creation");
remove_dir(path_str).expect("remove_dir should succeed in test");
assert!(
!exists(path_str),
"Directory should not exist after removal"
);
}
#[test]
fn test_create_dir_already_exists() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dir_path = temp_dir.path().join("existing_dir");
let path_str = dir_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir(path_str).expect("create_dir should succeed in test");
let result = create_dir(path_str);
assert!(result.is_err(), "Creating existing directory should fail");
}
#[test]
fn test_remove_nonexistent_dir() {
let result = remove_dir("/nonexistent/directory");
assert!(
result.is_err(),
"Removing nonexistent directory should fail"
);
}
#[test]
fn test_create_dir_all_nested() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let nested_path = temp_dir.path().join("a/b/c/d");
let path_str = nested_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir_all(path_str).expect("create_dir_all should succeed in test");
assert!(exists(path_str), "Nested directories should be created");
}
#[test]
fn test_create_dir_all_already_exists() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dir_path = temp_dir.path().join("existing");
let path_str = dir_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir_all(path_str).expect("create_dir_all should succeed in test");
let result = create_dir_all(path_str);
assert!(result.is_ok(), "create_dir_all should be idempotent");
}
#[test]
fn test_remove_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("to_remove.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
write(path_str, "content").expect("write should succeed in test");
assert!(exists(path_str), "File should exist");
remove_file(path_str).expect("remove_file should succeed in test");
assert!(!exists(path_str), "File should not exist after removal");
}
#[test]
fn test_remove_nonexistent_file() {
let result = remove_file("/nonexistent/file.txt");
assert!(result.is_err(), "Removing nonexistent file should fail");
}
#[test]
fn test_copy_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let source = temp_dir.path().join("source.txt");
let dest = temp_dir.path().join("dest.txt");
let source_str = source.to_str().expect("path should be valid UTF-8 in test");
let dest_str = dest.to_str().expect("path should be valid UTF-8 in test");
let content = "Copy me!";
write(source_str, content).expect("write should succeed in test");
let bytes_copied = copy(source_str, dest_str).expect("copy should succeed in test");
assert_eq!(bytes_copied as usize, content.len());
let dest_content = read_to_string(dest_str).expect("read_to_string should succeed in test");
assert_eq!(content, dest_content, "Copied content should match");
}
#[test]
fn test_copy_nonexistent_source() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dest = temp_dir.path().join("dest.txt");
let result = copy(
"/nonexistent/source.txt",
dest.to_str().expect("path should be valid UTF-8 in test"),
);
assert!(result.is_err(), "Copying nonexistent file should fail");
}
#[test]
fn test_rename_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let old_path = temp_dir.path().join("old_name.txt");
let new_path = temp_dir.path().join("new_name.txt");
let old_str = old_path
.to_str()
.expect("path should be valid UTF-8 in test");
let new_str = new_path
.to_str()
.expect("path should be valid UTF-8 in test");
write(old_str, "content").expect("write should succeed in test");
assert!(exists(old_str));
rename(old_str, new_str).expect("rename should succeed in test");
assert!(!exists(old_str), "Old path should not exist");
assert!(exists(new_str), "New path should exist");
}
#[test]
fn test_rename_nonexistent_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let new_path = temp_dir.path().join("new.txt");
let result = rename(
"/nonexistent/file.txt",
new_path
.to_str()
.expect("path should be valid UTF-8 in test"),
);
assert!(result.is_err(), "Renaming nonexistent file should fail");
}
#[test]
fn test_read_dir_empty() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let entries = read_dir(
temp_dir
.path()
.to_str()
.expect("path should be valid UTF-8 in test"),
)
.expect("read_dir should succeed in test");
assert_eq!(entries.len(), 0, "Empty directory should have no entries");
}
#[test]
fn test_read_dir_with_files() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let path_str = temp_dir
.path()
.to_str()
.expect("path should be valid UTF-8 in test");
write(&format!("{path_str}/file1.txt"), "content1").expect("write should succeed in test");
write(&format!("{path_str}/file2.txt"), "content2").expect("write should succeed in test");
let entries = read_dir(path_str).expect("read_dir should succeed in test");
assert_eq!(entries.len(), 2, "Directory should have 2 entries");
}
#[test]
fn test_read_nonexistent_dir() {
let result = read_dir("/nonexistent/directory");
assert!(result.is_err(), "Reading nonexistent directory should fail");
}
#[test]
fn test_metadata_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("meta.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
let content = "test content";
write(path_str, content).expect("write should succeed in test");
let meta = metadata(path_str).expect("metadata should succeed in test");
assert!(meta.is_file(), "Metadata should indicate file");
assert_eq!(meta.len(), content.len() as u64, "Size should match");
}
#[test]
fn test_metadata_dir() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dir_path = temp_dir.path().join("meta_dir");
let path_str = dir_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir(path_str).expect("create_dir should succeed in test");
let meta = metadata(path_str).expect("metadata should succeed in test");
assert!(meta.is_dir(), "Metadata should indicate directory");
}
#[test]
fn test_metadata_nonexistent() {
let result = metadata("/nonexistent/path");
assert!(result.is_err(), "Metadata of nonexistent path should fail");
}
#[test]
fn test_exists_true() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let file_path = temp_dir.path().join("test.txt");
std::fs::write(&file_path, "test").expect("Failed to write file");
assert!(exists(
file_path
.to_str()
.expect("path should be valid UTF-8 in test")
));
}
#[test]
fn test_exists_false() {
assert!(!exists("/nonexistent/path/file.txt"));
}
#[test]
fn test_exists_directory() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
assert!(
exists(
temp_dir
.path()
.to_str()
.expect("path should be valid UTF-8 in test")
),
"exists() should return true for directories"
);
}
}
#[cfg(test)]
mod property_tests {
use super::*;
use tempfile::TempDir;
#[test]
fn prop_write_read_round_trip() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let test_cases = [
"",
"Hello",
"Multi\nLine\nContent",
"Unicode: 世界🦀",
"Special: \t\r\n",
];
for (i, content) in test_cases.iter().enumerate() {
let file_path = temp_dir.path().join(format!("test_{i}.txt"));
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
write(path_str, content).expect("write should succeed in test");
let read_back =
read_to_string(path_str).expect("read_to_string should succeed in test");
assert_eq!(
*content, read_back,
"Round-trip should preserve content: {content:?}"
);
}
}
#[test]
fn prop_copy_creates_identical_file() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let source = temp_dir.path().join("source.txt");
let dest = temp_dir.path().join("dest.txt");
let source_str = source.to_str().expect("path should be valid UTF-8 in test");
let dest_str = dest.to_str().expect("path should be valid UTF-8 in test");
let content = "Original content";
write(source_str, content).expect("write should succeed in test");
copy(source_str, dest_str).expect("copy should succeed in test");
let source_content =
read_to_string(source_str).expect("read_to_string should succeed in test");
let dest_content = read_to_string(dest_str).expect("read_to_string should succeed in test");
assert_eq!(
source_content, dest_content,
"Copied file should have identical content"
);
}
#[test]
fn prop_rename_is_move_operation() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let old_path = temp_dir.path().join("old.txt");
let new_path = temp_dir.path().join("new.txt");
let old_str = old_path
.to_str()
.expect("path should be valid UTF-8 in test");
let new_str = new_path
.to_str()
.expect("path should be valid UTF-8 in test");
write(old_str, "content").expect("write should succeed in test");
assert!(exists(old_str), "Source should exist before rename");
rename(old_str, new_str).expect("rename should succeed in test");
assert!(!exists(old_str), "Source should not exist after rename");
assert!(exists(new_str), "Destination should exist after rename");
}
#[test]
fn prop_create_dir_all_idempotent() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let dir_path = temp_dir.path().join("a/b/c");
let path_str = dir_path
.to_str()
.expect("path should be valid UTF-8 in test");
create_dir_all(path_str).expect("create_dir_all should succeed in test");
assert!(exists(path_str));
let result = create_dir_all(path_str);
assert!(result.is_ok(), "create_dir_all should be idempotent");
assert!(exists(path_str));
}
#[test]
fn prop_file_ops_never_panic_on_invalid_paths() {
let invalid_paths = vec![
"",
"\0",
"/invalid/\0/path",
"/nonexistent/deep/nested/path/file.txt",
];
for path in invalid_paths {
let _ = read_to_string(path);
let _ = read(path);
let _ = write(path, "content");
let _ = create_dir(path);
let _ = create_dir_all(path);
let _ = remove_file(path);
let _ = remove_dir(path);
let _ = metadata(path);
let _ = read_dir(path);
let _ = exists(path);
}
}
#[test]
fn prop_exists_consistent_with_metadata() {
let temp_dir = TempDir::new().expect("TempDir::new should succeed in test");
let file_path = temp_dir.path().join("consistency.txt");
let path_str = file_path
.to_str()
.expect("path should be valid UTF-8 in test");
assert_eq!(
exists(path_str),
metadata(path_str).is_ok(),
"exists() should match metadata() success"
);
write(path_str, "content").expect("write should succeed in test");
assert_eq!(
exists(path_str),
metadata(path_str).is_ok(),
"exists() should match metadata() success after creation"
);
}
}