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 { 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(); } }