initial commit
This commit is contained in:
commit
749a232cf3
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/Cargo.lock
|
4
Cargo.toml
Normal file
4
Cargo.toml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[package]
|
||||||
|
name = "dropfile"
|
||||||
|
version = "1.0.0"
|
||||||
|
edition = "2021"
|
124
src/lib.rs
Normal file
124
src/lib.rs
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
use std::{fs::{self, File}, io::{self, ErrorKind, IoSlice, Seek, Write}, ops::{Deref, DerefMut}, path::Path};
|
||||||
|
|
||||||
|
pub struct DropFile {
|
||||||
|
path: Box<Path>,
|
||||||
|
file: Option<File>,
|
||||||
|
created: bool,
|
||||||
|
written_to: bool,
|
||||||
|
}
|
||||||
|
impl DropFile {
|
||||||
|
pub fn open<P: AsRef<Path>>(path: P, create: bool) -> Result<Self, &'static str> {
|
||||||
|
let path = path.as_ref();
|
||||||
|
|
||||||
|
let mut file_options = File::options();
|
||||||
|
file_options.read(true).write(true).create_new(create);
|
||||||
|
|
||||||
|
let file = file_options.open(path).map_err(|err| match err.kind() {
|
||||||
|
ErrorKind::AlreadyExists => "file already exists",
|
||||||
|
_ => "failed to open file"
|
||||||
|
})?;
|
||||||
|
|
||||||
|
return Ok(Self { path: path.into(), file: Some(file), created: create, written_to: false, });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete_file(&mut self) -> Result<(), &'static str> {
|
||||||
|
if self.file.take().is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
return fs::remove_file(&(self.path)).map_err(|_|
|
||||||
|
"failed to delete file"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
pub fn delete(mut self) -> Result<(), &'static str> {
|
||||||
|
return self.delete_file();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trunc(&mut self) -> Result<(), &'static str> {
|
||||||
|
let file = self.file.as_mut().unwrap();
|
||||||
|
file.rewind().map_err(|_| "failed to rewind file")?;
|
||||||
|
file.set_len(0).map_err(|_| "failed to truncate file")?;
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
pub fn trunc_to_cursor(&mut self) -> Result<(), &'static str> {
|
||||||
|
let file = self.file.as_mut().unwrap();
|
||||||
|
let cursor = file.stream_position().map_err(|_| "failed to get cursor position")?;
|
||||||
|
file.set_len(cursor).map_err(|_| "failed to truncate file")?;
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn path(&self) -> &Path {
|
||||||
|
return &(self.path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Drop for DropFile {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
if self.created && !self.written_to {
|
||||||
|
self.delete_file().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Deref for DropFile {
|
||||||
|
type Target = fs::File;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
return self.file.as_ref().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl DerefMut for DropFile {
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
return self.file.as_mut().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Write for DropFile {
|
||||||
|
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||||
|
self.written_to = true;
|
||||||
|
return self.deref_mut().write(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_vectored(&mut self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
|
||||||
|
self.written_to = true;
|
||||||
|
return self.deref_mut().write_vectored(bufs);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn flush(&mut self) -> io::Result<()> {
|
||||||
|
self.written_to = true;
|
||||||
|
return self.deref_mut().flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use {super::*, std::{assert_eq, io::Read, io::SeekFrom, path::Path}};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn new() {
|
||||||
|
drop(fs::remove_file("/tmp/dropfile"));
|
||||||
|
|
||||||
|
let mut file = DropFile::open("/tmp/dropfile", true).unwrap();
|
||||||
|
file.write("abcd".as_bytes()).unwrap();
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
let Err(_) = DropFile::open("/tmp/dropfile", true) else {
|
||||||
|
panic!("/tmp/dropfile created despite already existing");
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut file = DropFile::open("/tmp/dropfile", false).unwrap();
|
||||||
|
let mut buffer = [0u8; 3];
|
||||||
|
file.seek(SeekFrom::Start(1)).unwrap();
|
||||||
|
file.read_exact(&mut(buffer)).unwrap();
|
||||||
|
assert_eq!(buffer, "bcd".as_bytes());
|
||||||
|
file.trunc().unwrap();
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
let mut file = DropFile::open("/tmp/dropfile", false).unwrap();
|
||||||
|
let file_len = file.seek(SeekFrom::End(0)).unwrap();
|
||||||
|
assert_eq!(file_len, 0);
|
||||||
|
file.delete().unwrap();
|
||||||
|
|
||||||
|
let file = DropFile::open("/tmp/dropfile", true).unwrap();
|
||||||
|
drop(file);
|
||||||
|
|
||||||
|
assert!(!Path::new("/tmp/dropfile").exists());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user