mod path; pub(crate) use path::EitherPath; pub use path::{SafePath, WikPath}; use std::fmt; use camino::Utf8PathBuf; use tokio::{ fs::OpenOptions, io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt, BufWriter}, }; use crate::{WikError, error::FileOp}; // MARK: WikFile pub enum Open { /// Read Read, /// Read + Write (truncating) Write, /// Read + Write + Append Append, /// Read + Write + Create (truncating) Create, /// Read + Write + Append + Create CreateAppend, } pub struct WikFile { path: Utf8PathBuf, file: tokio::fs::File, } impl WikFile { pub async fn exists(path: Utf8PathBuf) -> Result { let result = tokio::fs::try_exists(&path).await; match result { Err(e) => Err(WikError::FileOperation { op: FileOp::Open, path, io: e, }), Ok(f) => Ok(f), } } pub async fn open(path: Utf8PathBuf, flags: Open) -> Result { let mut options = OpenOptions::new(); let result = match flags { Open::Read => options.read(true).open(&path), Open::Write => options.read(true).write(true).truncate(true).open(&path), Open::Append => options.read(true).append(true).open(&path), Open::Create => options .read(true) .write(true) .truncate(true) .create(true) .open(&path), Open::CreateAppend => options.read(true).append(true).create(true).open(&path), } .await; match result { Err(e) => Err(WikError::FileOperation { op: FileOp::Open, path, io: e, }), Ok(f) => Ok(WikFile { path, file: f }), } } pub async fn read_to_string(mut self) -> Result { let mut str = String::new(); match self.file.read_to_string(&mut str).await { Err(e) => Err(WikError::FileOperation { op: FileOp::Read, path: self.path, io: e, }), Ok(_) => Ok(str), } } pub async fn write_str(&mut self, str: &str) -> Result<(), WikError> { match self.file.write_all(str.as_bytes()).await { Err(e) => Err(WikError::FileOperation { op: FileOp::Write, path: self.path.clone(), io: e, }), Ok(_) => Ok(()), } } pub async fn write_all_strs>( &mut self, slice: I, ) -> Result<(), WikError> { let mut bufwrite = BufWriter::new(&mut self.file); for item in slice { let result = bufwrite.write_all(item.to_string().as_bytes()).await; if let Err(e) = result { return Err(WikError::FileOperation { op: FileOp::Write, path: self.path.clone(), io: e, }); } } bufwrite .flush() .await .map_err(|e| WikError::FileOperation { op: FileOp::Write, path: self.path.clone(), io: e, })?; Ok(()) } pub async fn seek_to_end(&mut self) -> Result<(), WikError> { match self.file.seek(std::io::SeekFrom::End(0)).await { Err(e) => Err(WikError::FileOperation { op: FileOp::Read, path: self.path.clone(), io: e, }), Ok(_) => Ok(()), } } pub async fn done(mut self) -> Result<(), WikError> { match self.file.flush().await { Err(e) => Err(WikError::FileOperation { op: FileOp::Write, path: self.path.clone(), io: e, }), Ok(_) => Ok(()), } } pub async fn remove(path: SafePath) -> Result<(), WikError> { match tokio::fs::remove_file(&path).await { Err(e) => Err(WikError::FileOperation { op: FileOp::Delete, path: path.0, io: e, }), Ok(_) => Ok(()), } } } pub async fn create_dir(path: SafePath) -> Result<(), WikError> { match tokio::fs::create_dir(&path).await { Err(_) => Err(WikError::DirectoryCreation { path: path.0 }), Ok(_) => Ok(()), } }