From a2f0f72d9a0503ecd9a30d8a1a27d116bdd16e5d Mon Sep 17 00:00:00 2001 From: werdl Date: Thu, 30 May 2024 06:48:08 +0100 Subject: [PATCH] initial commit --- .gitignore | 1 + Cargo.lock | 16 ++++ Cargo.toml | 7 ++ README.md | 36 +++++++++ file.txt | 0 src/defs.rs | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 24 ++++++ 7 files changed, 313 insertions(+) create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 README.md create mode 100644 file.txt create mode 100644 src/defs.rs create mode 100644 src/lib.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..9bbf915 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,16 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bitflags" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" + +[[package]] +name = "file" +version = "0.1.0" +dependencies = [ + "bitflags", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..fcfc7aa --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "file" +version = "0.1.0" +edition = "2021" + +[dependencies] +bitflags = "2.5.0" diff --git a/README.md b/README.md new file mode 100644 index 0000000..e55aab6 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# file +> An alternative filesystem API for Rust, for simplity. + +## Usage +```rust +use file::File; // all functions are implemented on File struct +use file::FileOptions; // options for file operations, a bitflag but can also be used like a builder + + +// read a file +let file: File = FileOptions::Read.open("README.md").unwrap(); + +let u8_vec: Vec = file.read().unwrap(); +let string: String = file.read_to_string().unwrap(); + + +// read to a file, then write it back prefixed with "Hello, " +let file: File = (FileOptions::Read | FileOptions::Write).open("README.md").unwrap(); + +let mut string: String = file.read_to_string().unwrap(); + +string = format!("Hello, {}", string); + +let _ = file.seek(0); + +file.write(string.as_bytes()).unwrap(); + + +// create a file and write to it +let file: File = FileOptions::new() + .write(true) + .create(true) + .open("hello.txt"); + +let _ = file.write("Hello, world!"); +``` \ No newline at end of file diff --git a/file.txt b/file.txt new file mode 100644 index 0000000..e69de29 diff --git a/src/defs.rs b/src/defs.rs new file mode 100644 index 0000000..3e0a5c2 --- /dev/null +++ b/src/defs.rs @@ -0,0 +1,229 @@ +use std::io::{Read, Seek, Write}; + +use bitflags::bitflags; + +bitflags! { + #[derive(Debug, Clone, Copy)] + pub struct FileOptions: u32 { + const Read = 0b00000001; + const Write = 0b00000010; + const Create = 0b00000100; + const ExclusiveCreate = 0b00001000; + const Truncate = 0b00010000; + const Append = 0b00100000; + + const Uninitialized = 0b10000000; + } +} + +impl FileOptions { + fn is_initialized(&self) -> bool { + !self.contains(FileOptions::Uninitialized) + } + + pub fn read(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::Read + } else { + self + } + } + + pub fn write(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::Write + } else { + self + } + } + + pub fn create(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::Create + } else { + self + } + } + + pub fn exclusive_create(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::ExclusiveCreate + } else { + self + } + } + + pub fn truncate(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::Truncate + } else { + self + } + } + + pub fn append(self, set: bool) -> FileOptions { + if set { + (self - FileOptions::Uninitialized) | FileOptions::Append + } else { + self + } + } + + pub fn new() -> FileOptions { + FileOptions::Uninitialized + } + + pub fn open(self, file_name: T) -> Result + where + T: ToString, + { + if self.contains(FileOptions::Uninitialized) { + return Err(FileError { + message: "FileOptions uninitialized".to_string(), + file_options: self, + file_name: file_name.to_string(), + underlying_error: std::io::Error::new(std::io::ErrorKind::InvalidInput, "FileOptions uninitialized"), + }); + } + + let openoptions = std::fs::OpenOptions::new() + .read(self.contains(FileOptions::Read)) + .write(self.contains(FileOptions::Write)) + .create(self.contains(FileOptions::Create)) + .create_new(self.contains(FileOptions::ExclusiveCreate)) + .truncate(self.contains(FileOptions::Truncate)) + .append(self.contains(FileOptions::Append)) + .open(file_name.to_string()); + + match openoptions { + Ok(file) => Ok(File { + file_name: file_name.to_string(), + file_options: self, + underlying_file: file, + }), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self, + file_name: file_name.to_string(), + underlying_error: e, + }), + } + + } +} + +pub struct File { + file_name: String, + file_options: FileOptions, + pub underlying_file: std::fs::File, +} + +// error struct +#[derive(Debug)] +pub struct FileError { + message: String, + file_options: FileOptions, + file_name: String, + underlying_error: std::io::Error, +} + +impl File { + pub fn read(&mut self) -> Result { + let mut buffer = String::new(); + match self.underlying_file.read_to_string(&mut buffer) { + Ok(_) => { + println!("Read {} bytes from file", buffer.len()); + Ok(buffer) + }, + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } + + pub fn read_u8(&mut self) -> Result, FileError> { + let string = self.read(); + + match string { + Ok(s) => Ok(s.into_bytes()), + Err(e) => Err(e), + } + } + + pub fn write(&mut self, data: T) -> Result<(), FileError> { + match self.underlying_file.write_all(data.to_string().as_bytes()) { + Ok(_) => Ok(()), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } + + pub fn write_u8(&mut self, data: Vec) -> Result<(), FileError> { + let string = String::from_utf8(data); + + match string { + Ok(s) => self.write(s), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: std::io::Error::new(std::io::ErrorKind::InvalidData, e), + }), + } + } + + pub fn flush(&mut self) -> Result<(), FileError> { + match self.underlying_file.flush() { + Ok(_) => Ok(()), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } + + pub fn delete(self) -> Result<(), FileError> { + match std::fs::remove_file(self.file_name.clone()) { + Ok(_) => Ok(()), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } + + pub fn close(self) -> Result<(), FileError> { + match self.underlying_file.sync_all() { + Ok(_) => Ok(()), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } + + pub fn seek(&mut self, pos: u32) -> Result { + match self.underlying_file.seek(std::io::SeekFrom::Start(pos as u64)) { + Ok(pos) => Ok(pos), + Err(e) => Err(FileError { + message: e.to_string(), + file_options: self.file_options, + file_name: self.file_name.clone(), + underlying_error: e, + }), + } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..8f79740 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,24 @@ +mod defs; + + +mod tests { + use crate::defs::{File, FileOptions}; + use std::io::Read; + + + #[test] + fn open() { + let file = (FileOptions::Create | FileOptions::Write).open("file.txt"); + assert_eq!(file.is_ok(), true); + } + + #[test] + fn open_and_attempt_read() { + let file = (FileOptions::Read).open("README.md"); + assert_eq!(file.is_ok(), true); + let mut file: File = file.unwrap(); + + // read the file + let contents = file.read(); + } +} \ No newline at end of file