use std::fs::File;
use std::io::Read;
use std::path::Path;
use crate::error::PctxError;
pub fn read_file_contents(
path: &Path,
max_size: u64,
_encoding: Option<&str>,
) -> Result<String, PctxError> {
let file = File::open(path).map_err(|e| {
if e.kind() == std::io::ErrorKind::NotFound {
PctxError::FileNotFound(path.to_path_buf())
} else if e.kind() == std::io::ErrorKind::PermissionDenied {
PctxError::PermissionDenied(path.to_path_buf())
} else {
PctxError::Io(e)
}
})?;
let metadata = file.metadata().map_err(PctxError::Io)?;
if metadata.len() > max_size {
return Err(PctxError::FileTooLarge {
path: path.to_path_buf(),
size: metadata.len(),
max: max_size,
});
}
let mut bytes = Vec::new();
file.take(max_size + 1)
.read_to_end(&mut bytes)
.map_err(PctxError::Io)?;
if bytes.len() as u64 > max_size {
return Err(PctxError::FileTooLarge {
path: path.to_path_buf(),
size: bytes.len() as u64, max: max_size,
});
}
match String::from_utf8(bytes) {
Ok(content) => Ok(content),
Err(e) => {
Ok(String::from_utf8_lossy(e.as_bytes()).into_owned())
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write;
use tempfile::NamedTempFile;
const MAX_SIZE: u64 = 10 * 1024 * 1024;
#[test]
fn test_read_utf8_file() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "Hello, world!").unwrap();
writeln!(file, "This is a test.").unwrap();
let content = read_file_contents(file.path(), MAX_SIZE, None).unwrap();
assert!(content.contains("Hello, world!"));
assert!(content.contains("This is a test."));
}
#[test]
fn test_read_file_with_unicode() {
let mut file = NamedTempFile::new().unwrap();
writeln!(file, "Hello, 世界!").unwrap();
writeln!(file, "Привет мир!").unwrap();
writeln!(file, "🎉🎊🎈").unwrap();
let content = read_file_contents(file.path(), MAX_SIZE, None).unwrap();
assert!(content.contains("世界"));
assert!(content.contains("Привет"));
assert!(content.contains("🎉"));
}
#[test]
fn test_read_empty_file() {
let file = NamedTempFile::new().unwrap();
let content = read_file_contents(file.path(), MAX_SIZE, None).unwrap();
assert!(content.is_empty());
}
#[test]
fn test_read_nonexistent_file() {
let result = read_file_contents(Path::new("/nonexistent/file.txt"), MAX_SIZE, None);
assert!(result.is_err());
match result.unwrap_err() {
PctxError::FileNotFound(_) => {}
e => panic!("Expected FileNotFound, got {:?}", e),
}
}
#[test]
fn test_read_file_with_invalid_utf8() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(b"Valid text ").unwrap();
file.write_all(&[0xFF, 0xFE]).unwrap(); file.write_all(b" more text").unwrap();
let content = read_file_contents(file.path(), MAX_SIZE, None).unwrap();
assert!(content.contains("Valid text"));
assert!(content.contains("more text"));
assert!(content.contains('\u{FFFD}'));
}
#[test]
fn test_read_file_too_large() {
let mut file = NamedTempFile::new().unwrap();
file.write_all(b"1234567890").unwrap();
let result = read_file_contents(file.path(), 5, None);
assert!(result.is_err());
match result.unwrap_err() {
PctxError::FileTooLarge { size, max, .. } => {
assert_eq!(size, 10);
assert_eq!(max, 5);
}
e => panic!("Expected FileTooLarge, got {:?}", e),
}
}
}