use super::{
config::FilesystemConfig,
error::FileReadError,
path::{resolve_buffer_path, resolve_buffer_path_from},
platform::open_existing_file,
};
use std::{
io::Read,
path::{Path, PathBuf},
};
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct ReadTextFile {
pub path: PathBuf,
pub bytes: Vec<u8>,
}
pub fn read_existing_file(
requested_path: impl AsRef<Path>,
config: &FilesystemConfig,
max_file_bytes: u64,
) -> Result<ReadTextFile, FileReadError> {
let resolved =
resolve_buffer_path(requested_path.as_ref(), config).map_err(FileReadError::Policy)?;
if !resolved.exists {
return Err(FileReadError::Missing {
path: resolved.path,
});
}
read_resolved_existing_file(resolved.path, config, max_file_bytes)
}
pub fn read_existing_workspace_file(
workspace_relative_path: impl AsRef<Path>,
config: &FilesystemConfig,
max_file_bytes: u64,
) -> Result<ReadTextFile, FileReadError> {
let resolved = resolve_buffer_path_from(
workspace_relative_path.as_ref(),
config,
&config.workspace_root,
)
.map_err(FileReadError::Policy)?;
if !resolved.exists {
return Err(FileReadError::Missing {
path: resolved.path,
});
}
read_resolved_existing_file(resolved.path, config, max_file_bytes)
}
pub fn read_text_file(
requested_path: impl AsRef<Path>,
config: &FilesystemConfig,
) -> Result<ReadTextFile, FileReadError> {
let resolved =
resolve_buffer_path(requested_path.as_ref(), config).map_err(FileReadError::Policy)?;
if !resolved.exists {
return Ok(ReadTextFile {
path: resolved.path,
bytes: Vec::new(),
});
}
read_resolved_existing_file(resolved.path, config, config.max_file_bytes)
}
fn read_resolved_existing_file(
path: PathBuf,
config: &FilesystemConfig,
max_file_bytes: u64,
) -> Result<ReadTextFile, FileReadError> {
let (mut file, metadata) = open_existing_file(&path, config)?;
if !metadata.is_file() {
return Err(FileReadError::UnsupportedFileType { path });
}
if metadata.len() > max_file_bytes {
return Err(FileReadError::TooLarge {
path,
size: metadata.len(),
max_size: max_file_bytes,
});
}
let mut bytes = Vec::new();
let mut bounded = (&mut file).take(max_file_bytes.saturating_add(1));
let _bytes_read = bounded
.read_to_end(&mut bytes)
.map_err(|source| FileReadError::Read {
path: path.clone(),
source,
})?;
if u64::try_from(bytes.len()).unwrap_or(u64::MAX) > max_file_bytes {
return Err(FileReadError::TooLarge {
path,
size: u64::try_from(bytes.len()).unwrap_or(u64::MAX),
max_size: max_file_bytes,
});
}
Ok(ReadTextFile { path, bytes })
}