use std::fs::{self, File, OpenOptions};
use std::io::{BufRead, BufReader, Write};
use std::path::Path;
use crate::error::{EditorError, Result};
pub struct FileHandler;
impl FileHandler {
pub fn read_file(path: &Path) -> Result<Vec<String>> {
if !path.exists() {
return Err(EditorError::FileNotFound(path.to_path_buf()));
}
let metadata = fs::metadata(path)?;
if metadata.permissions().readonly() && !metadata.is_file() {
return Err(EditorError::PermissionDenied(path.to_path_buf()));
}
let file = File::open(path).map_err(|e| {
if e.kind() == std::io::ErrorKind::PermissionDenied {
EditorError::PermissionDenied(path.to_path_buf())
} else {
EditorError::IoError(e)
}
})?;
let reader = BufReader::new(file);
let mut lines = Vec::new();
for line_result in reader.lines() {
let line = line_result.map_err(|e| {
if e.kind() == std::io::ErrorKind::InvalidData {
EditorError::InvalidUtf8
} else {
EditorError::IoError(e)
}
})?;
lines.push(line);
}
if lines.is_empty() {
lines.push(String::new());
}
Ok(lines)
}
pub fn write_file(path: &Path, lines: &[String]) -> Result<()> {
if let Some(parent) = path.parent() {
if !parent.exists() {
fs::create_dir_all(parent)?;
}
}
let mut file = OpenOptions::new()
.write(true)
.create(true)
.truncate(true)
.open(path)
.map_err(|e| {
if e.kind() == std::io::ErrorKind::PermissionDenied {
EditorError::PermissionDenied(path.to_path_buf())
} else {
EditorError::IoError(e)
}
})?;
for (i, line) in lines.iter().enumerate() {
file.write_all(line.as_bytes())?;
if i < lines.len() - 1 || !line.is_empty() {
file.write_all(b"\n")?;
}
}
file.flush()?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs;
use tempfile::tempdir;
#[test]
fn test_read_file_success() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test.txt");
fs::write(&file_path, "line1\nline2\nline3").unwrap();
let lines = FileHandler::read_file(&file_path).unwrap();
assert_eq!(lines, vec!["line1", "line2", "line3"]);
}
#[test]
fn test_read_file_with_crlf() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("test_crlf.txt");
fs::write(&file_path, "line1\r\nline2\r\nline3").unwrap();
let lines = FileHandler::read_file(&file_path).unwrap();
assert_eq!(lines, vec!["line1", "line2", "line3"]);
}
#[test]
fn test_read_empty_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("empty.txt");
fs::write(&file_path, "").unwrap();
let lines = FileHandler::read_file(&file_path).unwrap();
assert_eq!(lines, vec![""]);
}
#[test]
fn test_read_file_not_found() {
let result = FileHandler::read_file(Path::new("/nonexistent/file.txt"));
assert!(matches!(result, Err(EditorError::FileNotFound(_))));
}
#[test]
fn test_write_file_success() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("output.txt");
let lines = vec!["line1".to_string(), "line2".to_string(), "line3".to_string()];
FileHandler::write_file(&file_path, &lines).unwrap();
let content = fs::read_to_string(&file_path).unwrap();
assert_eq!(content, "line1\nline2\nline3\n");
}
#[test]
fn test_write_empty_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("empty_output.txt");
let lines = vec!["".to_string()];
FileHandler::write_file(&file_path, &lines).unwrap();
let content = fs::read_to_string(&file_path).unwrap();
assert_eq!(content, "");
}
#[test]
fn test_write_file_creates_directory() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("subdir").join("output.txt");
let lines = vec!["test".to_string()];
FileHandler::write_file(&file_path, &lines).unwrap();
assert!(file_path.exists());
let content = fs::read_to_string(&file_path).unwrap();
assert_eq!(content, "test\n");
}
#[test]
fn test_read_utf8_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("utf8.txt");
fs::write(&file_path, "สวัสดี\n世界\n🦀").unwrap();
let lines = FileHandler::read_file(&file_path).unwrap();
assert_eq!(lines, vec!["สวัสดี", "世界", "🦀"]);
}
#[test]
fn test_write_utf8_file() {
let dir = tempdir().unwrap();
let file_path = dir.path().join("utf8_output.txt");
let lines = vec!["สวัสดี".to_string(), "世界".to_string(), "🦀".to_string()];
FileHandler::write_file(&file_path, &lines).unwrap();
let content = fs::read_to_string(&file_path).unwrap();
assert_eq!(content, "สวัสดี\n世界\n🦀\n");
}
}