use std::path::{Path, PathBuf};
use tokio::{
fs,
io::{self, AsyncReadExt, AsyncWriteExt},
};
use crate::split::{Split, SplitResult};
pub trait SplitAsyncExt {
fn run_async(
&self
) -> impl std::future::Future<Output = io::Result<SplitResult>> + Send;
}
impl SplitAsyncExt for Split {
async fn run_async(&self) -> io::Result<SplitResult> {
let in_file: &Path = match self.in_file {
| Some(ref p) => {
let p: &Path = p.as_ref();
if !p.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
"in_file path not found",
));
}
if !p.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"in_file is not a path to file",
));
}
p
},
| None => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"in_file is not set",
));
},
};
let out_dir: &Path = match self.out_dir {
| Some(ref p) => {
let p: &Path = p.as_ref();
if !p.exists() {
fs::create_dir_all(&p).await?;
} else {
if p.is_file() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"out_dir is not a directory",
));
}
}
p
},
| None => {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"out_dir is not set",
));
},
};
let chunk_size: usize = self.chunk_size;
let buffer_capacity: usize = chunk_size.min(self.cap_max);
let input: fs::File =
fs::OpenOptions::new().read(true).open(in_file).await?;
let file_size: usize = input.metadata().await?.len() as usize;
let mut reader: io::BufReader<fs::File> =
io::BufReader::with_capacity(buffer_capacity, input);
let mut buffer: Vec<u8> = vec![0; chunk_size];
let mut total_chunks: usize = 0;
let mut current: usize = 0;
loop {
let read: usize = reader.read(&mut buffer[current..]).await?;
if read == 0 {
if current > 0 {
let output_path: PathBuf =
out_dir.join(total_chunks.to_string());
let output: fs::File = fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(output_path)
.await?;
let mut writer: io::BufWriter<fs::File> =
io::BufWriter::with_capacity(buffer_capacity, output);
writer.write_all(&buffer[..current]).await?;
writer.flush().await?;
total_chunks += 1;
}
break;
}
current += read;
if current >= chunk_size {
let output_path: PathBuf =
out_dir.join(total_chunks.to_string());
let output: fs::File = fs::OpenOptions::new()
.create(true)
.truncate(true)
.write(true)
.open(output_path)
.await?;
let mut writer: io::BufWriter<fs::File> =
io::BufWriter::with_capacity(buffer_capacity, output);
writer.write_all(&buffer[..chunk_size]).await?;
writer.flush().await?;
total_chunks += 1;
buffer.copy_within(chunk_size..current, 0);
current -= chunk_size;
}
}
Ok(SplitResult { file_size, total_chunks })
}
}