use core::fmt;
use std::ffi::OsString;
use std::mem;
use std::{path::PathBuf, sync::Arc};
use std::io::{ Result as IoResult, ErrorKind, Error};
use pi_share::{ShareMutex};
use pi_async_file::file::{WriteOptions, AsyncFileOptions};
use pi_rt_file::{SafeFile, remove_file, rename};
use crate::utils;
pub const TMP_SUFFIX_FILE_NAME: &'static str = ".tmp.dl";
pub const SEG_SUFFIX_FILE_NAME: &'static str = ".seg.dl";
pub const DATA_LIMIT: u64 = 256*1024*1024;
#[derive(Debug)]
pub struct FileInfo {
pub dir: PathBuf,
pub file: PathBuf,
pub temp_dir: PathBuf,
pub data_limit: usize,
}
pub struct TempFile {
pub info: FileInfo,
pub temp_file: PathBuf,
pub seg_file: PathBuf,
temp_file_handle: SafeFile,
seg_file_handle: SafeFile,
pub data: ShareMutex<(u64, Vec<u8>)>,
}
impl fmt::Debug for TempFile {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let pos_len = {
let data = self.data.lock();
(data.0, data.1.len())
};
f.debug_struct("TempFile").field("info", &self.info).field("data", &pos_len).finish()
}
}
impl TempFile {
pub async fn open(info: FileInfo) -> IoResult<(Self, Vec<u8>)> {
let dir = if info.temp_dir.as_path().as_os_str().is_empty() {
info.dir.as_path()
}else{
info.temp_dir.as_path()
};
let mut temp_file = dir.join(&info.file);
let mut seg_file = temp_file.clone();
if let Some(ext) = temp_file.extension() {
let mut s = OsString::from(ext);
let mut s1 = s.clone();
s.push(TMP_SUFFIX_FILE_NAME);
temp_file.set_extension(s);
s1.push(SEG_SUFFIX_FILE_NAME);
seg_file.set_extension(s1);
}else{
temp_file.set_extension(TMP_SUFFIX_FILE_NAME);
seg_file.set_extension(SEG_SUFFIX_FILE_NAME);
}
let b = seg_file.exists();
let temp_file_handle = SafeFile::open(temp_file.clone(), AsyncFileOptions::OnlyWrite).await?;
let seg_file_handle = SafeFile::open(seg_file.clone(), AsyncFileOptions::ReadWrite).await?;
let file = TempFile {
info,
temp_file,
seg_file,
temp_file_handle,
seg_file_handle,
data: ShareMutex::new((0, vec![])),
};
let v = if b {
let meta = file.seg_file.metadata()?;
file.seg_file_handle.read(0, meta.len() as usize).await?
}else{vec![]};
Ok((file, v))
}
pub async fn write_segments(&self, segments: Vec<u8>) -> IoResult<()> {
let _ = self.seg_file_handle.write(0, Arc::from(segments.as_slice()), WriteOptions::None).await?;
Ok(())
}
pub async fn write_data(&self, pos: u64, buf: Box<[u8]>, len: usize) -> IoResult<()> {
{
let mut data = self.data.lock();
if data.1.len() == 0 {
data.0 = pos;
utils::write(&mut data.1, 0, &buf[..len], 0);
}else if pos >= data.0 { let pos = (pos - data.0) as usize;
utils::write(&mut data.1, pos, &buf[..len], 0);
}
}
let buf = Arc::from(&buf[..len]);
let _ = self.temp_file_handle.write(pos, buf, WriteOptions::None).await?;
Ok(())
}
pub async fn finished(&self) -> IoResult<()> {
remove_file(self.seg_file.clone()).await?;
let dir = self.info.dir.as_path();
let file = dir.join(&self.info.file);
rename(self.temp_file.clone(), file).await
}
pub async fn take(&self) -> IoResult<Vec<u8>> {
let dir = self.info.dir.as_path();
let file = dir.join(&self.info.file);
if !file.exists() {
return Err(Error::new(
ErrorKind::NotFound,
format!("take fail, file: {:?}", file),
))
}
let meta = file.metadata()?;
let mut data = self.data.lock();
if data.0 == 0 && data.1.len() as u64 == meta.len() {
return Ok(mem::replace(&mut data.1, vec![]))
}
todo!()
}
}