use std::path::Path;
#[cfg(feature = "tokio")]
use std::path::PathBuf;
use ropey::Rope;
#[cfg(feature = "tokio")]
use tokio::io::AsyncReadExt;
pub enum LoadProgress {
Reading {
bytes_read: usize,
total_bytes: Option<usize>,
},
Complete(Rope),
Error(anyhow::Error),
}
pub struct AsyncFileLoader;
impl AsyncFileLoader {
pub fn new() -> Self {
Self
}
#[cfg(feature = "tokio")]
pub async fn load_file_async<F>(path: PathBuf, mut callback: F) -> anyhow::Result<Rope>
where
F: FnMut(LoadProgress),
{
use tokio::fs;
let total_bytes = fs::metadata(&path).await.ok().map(|m| m.len() as usize);
callback(LoadProgress::Reading {
bytes_read: 0,
total_bytes,
});
let chunk_size = 64 * 1024; let mut bytes_read = 0usize;
let mut content = Vec::new();
let mut file = fs::File::open(&path).await?;
let mut buffer = vec![0u8; chunk_size];
loop {
match file.read(&mut buffer).await {
Ok(0) => break, Ok(n) => {
content.extend_from_slice(&buffer[..n]);
bytes_read += n;
callback(LoadProgress::Reading {
bytes_read,
total_bytes,
});
}
Err(e) => {
let msg = e.to_string();
callback(LoadProgress::Error(anyhow::anyhow!(msg.clone())));
return Err(anyhow::anyhow!(msg));
}
}
}
let text = String::from_utf8_lossy(&content).to_string();
let rope = Rope::from_str(&text);
callback(LoadProgress::Complete(rope.clone()));
Ok(rope)
}
pub fn load_file_sync(path: &Path) -> anyhow::Result<Rope> {
let content = std::fs::read_to_string(path)?;
Ok(Rope::from_str(&content))
}
pub fn load_to_string_sync(path: &Path) -> anyhow::Result<String> {
Ok(std::fs::read_to_string(path)?)
}
}
impl Default for AsyncFileLoader {
fn default() -> Self {
Self::new()
}
}
pub struct LargeFileConfig {
pub async_threshold: usize,
pub chunk_size: usize,
}
impl Default for LargeFileConfig {
fn default() -> Self {
Self {
async_threshold: 10 * 1024 * 1024,
chunk_size: 64 * 1024,
}
}
}
pub fn load_file_auto(path: &Path, _config: &LargeFileConfig) -> anyhow::Result<FileLoadResult> {
let metadata = std::fs::metadata(path)?;
let file_size = metadata.len() as usize;
let content = std::fs::read_to_string(path)?;
let rope = Rope::from_str(&content);
Ok(FileLoadResult { rope, file_size })
}
pub struct FileLoadResult {
pub rope: Rope,
pub file_size: usize,
}
impl FileLoadResult {
pub fn rope(&self) -> &Rope {
&self.rope
}
pub fn file_size(&self) -> usize {
self.file_size
}
pub fn is_large(&self) -> bool {
self.file_size > 10 * 1024 * 1024
}
pub fn line_count(&self) -> usize {
use ropey::LineType;
self.rope.len_lines(LineType::LF)
}
}