mod buffered_file;
mod circular_path_buffer;
mod directory_entry;
mod directory_listing;
mod entry;
mod operations;
pub use buffered_file::*;
pub use circular_path_buffer::*;
pub use directory_entry::*;
pub use directory_listing::*;
pub use entry::*;
use operations::*;
use std::fs::{OpenOptions, metadata};
use std::os::unix::ffi::OsStrExt;
use std::path::{Component, Path, PathBuf};
fn is_blank_path(path: &Path) -> bool {
path == PathBuf::new()
}
pub struct FileDevice {
/// The path to which the file device is confined. Files and directories
/// outside of this directory cannot be accessed.
pub base_path: PathBuf,
pub open_buffer: CircularPathBuffer,
pub move_buffer: CircularPathBuffer,
pub name_buffer: CircularPathBuffer,
pub entry: Option<(Entry, PathBuf)>,
pub move_success: bool,
pub new_pointer: u32,
pub new_length: u32,
}
impl FileDevice {
pub fn new() -> Self {
Self {
base_path: PathBuf::from("/home/ben/Sandbox"),
open_buffer: CircularPathBuffer::new(),
move_buffer: CircularPathBuffer::new(),
name_buffer: CircularPathBuffer::new(),
entry: None,
move_success: false,
new_pointer: 0,
new_length: 0,
}
}
pub fn flush_entry(&mut self) {
if let Some((Entry::File(buffered_file), _)) = &mut self.entry {
buffered_file.flush();
}
}
pub fn close_entry(&mut self) {
self.open_buffer.clear();
self.move_buffer.clear();
self.name_buffer.clear();
self.flush_entry();
self.entry = None;
self.new_pointer = 0;
self.new_length = 0;
}
pub fn write_to_open_port(&mut self, byte: u8) {
if let Some(relative_path) = self.open_buffer.push_byte(byte) {
self.close_entry();
if !is_blank_path(&relative_path) {
if let Ok(path) = self.attach_base(&relative_path) {
println!("Attempting to open entry at: {path:?}");
let _ = self.open_entry(&path);
};
}
}
}
pub fn set_name_pointer(&mut self, byte: u8) {
self.name_buffer.set_pointer(byte);
}
/// Open the entry at the given path.
pub fn open_entry(&mut self, path: &Path) -> Result<(), ()> {
macro_rules! raise_on_err {
($res:expr) => {match $res {Ok(v)=>v, Err(_)=>return Err(())} } }
if !path.starts_with(&self.base_path) { return Err(()); }
let metadata = raise_on_err!(metadata(&path));
if metadata.is_file() {
if let Ok(file) = OpenOptions::new().read(true).write(true).open(&path) {
self.close_entry();
let file_entry = Entry::File(BufferedFile::new(file));
self.entry = Some((file_entry, path.to_owned()));
let relative = remove_base(&path, &self.base_path).unwrap();
self.name_buffer.populate(relative.as_os_str().as_bytes());
return Ok(());
};
} else if metadata.is_dir() {
if let Ok(listing) = DirectoryListing::from_path(&path, &self.base_path) {
let dir_entry = Entry::Directory(listing);
self.entry = Some((dir_entry, path.to_owned()));
let relative = remove_base(&path, &self.base_path).unwrap();
self.name_buffer.populate(relative.as_os_str().as_bytes());
return Ok(());
};
};
return Err(());
}
pub fn write_to_move_port(&mut self, byte: u8) {
if let Some(dest) = self.move_buffer.push_byte(byte) {
if let Some((_, source)) = &self.entry {
if is_blank_path(&dest) {
eprintln!("Attempting to delete entry at: {source:?}");
self.move_success = delete_entry(&source);
} else if let Ok(destination) = self.attach_base(&dest) {
eprintln!("Attempting to move entry to: {destination:?}");
self.move_success = move_entry(&source, &destination);
}
} else {
if is_blank_path(&dest) {
self.move_success = false;
} else if let Ok(destination) = self.attach_base(&dest) {
eprintln!("Attempting to create entry at: {destination:?}");
self.move_success = create_file(&destination);
}
}
self.close_entry();
}
}
/// Attempt to open the parent directory of the current entry.
pub fn ascend_to_parent(&mut self) {
if let Some((_, path)) = &self.entry {
if let Some(parent_path) = path.parent() {
let path = parent_path.to_owned();
let _ = self.open_entry(&path);
}
}
}
/// Attempt to open the currently-selected child.
pub fn descend_to_child(&mut self) {
if let Some((Entry::Directory(listing), _)) = &self.entry {
if let Some(child_path) = listing.child_path() {
if let Ok(child_path) = self.attach_base(&child_path) {
let child_path = child_path.to_owned();
let _ = self.open_entry(&child_path);
}
};
}
}
/// Return true if the currently-open entry is a directory.
pub fn entry_type(&self) -> bool {
match self.entry {
Some((Entry::Directory(_), _)) => true,
_ => false,
}
}
/// Return true if the currently-selected child is a directory.
pub fn read_child_name(&mut self) -> u8 {
if let Some((Entry::Directory(listing), _)) = &mut self.entry {
listing.child_name().read_byte()
} else {
0
}
}
pub fn set_child_name_pointer(&mut self, byte: u8) {
if let Some((Entry::Directory(listing), _)) = &mut self.entry {
listing.child_name().set_pointer(byte);
}
}
/// Return true if the currently-selected child is a directory.
pub fn child_type(&self) -> bool {
if let Some((Entry::Directory(listing), _)) = &self.entry {
match listing.child_type() {
Some(EntryType::Directory) => true,
Some(EntryType::File) => false,
None => false,
}
} else {
false
}
}
pub fn read_byte(&mut self) -> u8 {
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.read_byte(),
_ => 0,
}
}
pub fn write_byte(&mut self, byte: u8) {
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.write_byte(byte),
_ => (),
}
}
pub fn pointer(&mut self) -> u32 {
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.pointer(),
Some((Entry::Directory(listing), _)) => listing.selected(),
_ => 0,
}
}
pub fn commit_pointer(&mut self) {
let pointer = std::mem::take(&mut self.new_pointer);
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.set_pointer(pointer),
Some((Entry::Directory(listing), _)) => listing.set_selected(pointer),
_ => (),
}
}
pub fn length(&mut self) -> u32 {
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.length(),
Some((Entry::Directory(listing), _)) => listing.length(),
_ => 0,
}
}
pub fn commit_length(&mut self) {
let length = std::mem::take(&mut self.new_length);
match &mut self.entry {
Some((Entry::File(buffered_file), _)) => buffered_file.set_length(length),
_ => (),
}
}
fn attach_base(&self, relative_path: &Path) -> Result<PathBuf, ()> {
let mut full_path = self.base_path.clone();
let mut has_root = false;
for component in relative_path.components() {
match component {
Component::Normal(s) => full_path.push(s),
Component::ParentDir => return Err(()),
Component::CurDir => continue,
Component::RootDir => has_root = true,
Component::Prefix(_) => continue,
}
};
match has_root {
true => Ok(full_path),
false => Err(())
}
}
}
impl Drop for FileDevice {
fn drop(&mut self) {
self.close_entry();
}
}