mod bedrock_file_path; mod buffered_file; mod circular_path_buffer; mod directory_child; mod directory_listing; mod entry; mod operations; pub use bedrock_file_path::*; pub use buffered_file::*; pub use circular_path_buffer::*; pub use directory_child::*; pub use directory_listing::*; pub use entry::*; use operations::*; use std::path::{Component, Path, PathBuf}; pub struct FileDevice { pub base_path: PathBuf, pub default_path: PathBuf, pub open_buffer: CircularPathBuffer, pub move_buffer: CircularPathBuffer, pub name_buffer: CircularPathBuffer, pub entry: Option<(Entry, BedrockFilePath)>, pub success: bool, pub pointer: u32, pub length: u32, pub enable_read: bool, pub enable_write: bool, pub enable_create: bool, pub enable_move: bool, pub enable_delete: bool, } impl FileDevice { pub fn new() -> Self { #[cfg(target_family = "unix")] let default_base: PathBuf = PathBuf::from("/"); #[cfg(target_family = "windows")] let default_base: PathBuf = PathBuf::from(""); Self { base_path: default_base, default_path: match std::env::current_dir() { Ok(dir) => PathBuf::from(dir), Err(_) => PathBuf::from(""), }, open_buffer: CircularPathBuffer::new(), move_buffer: CircularPathBuffer::new(), name_buffer: CircularPathBuffer::new(), entry: None, success: false, pointer: 0, length: 0, enable_read: true, enable_write: true, enable_create: true, enable_move: true, enable_delete: false, } } /// Commit any pending writes to the currently-open file. pub fn flush_entry(&mut self) { if let Some((Entry::File(buffered_file), _)) = &mut self.entry { buffered_file.flush(); } } /// Safely close the currently-open entry, cleaning up entry variables. 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.pointer = 0; self.length = 0; } /// Process a byte received from the OPEN port. pub fn write_to_open_port(&mut self, byte: u8) { if let Some(buffer) = self.open_buffer.push_byte(byte) { self.close_entry(); if let Some(path) = BedrockFilePath::from_buffer(buffer, &self.base_path) { self.success = self.open_entry(path).is_ok(); } } } /// Opens the entry at the given path. pub fn open_entry(&mut self, path: BedrockFilePath) -> Result<(), ()> { macro_rules! unres { ($result:expr) => { match $result {Ok(v)=>v,Err(_)=>return Err(())} }; } let absolute_path = path.as_path(); let metadata = unres!(std::fs::metadata(&absolute_path)); if metadata.is_file() { let open_result = std::fs::OpenOptions::new() .read(self.enable_read) .write(self.enable_write) .open(&absolute_path); // Keep the current entry open if we can't open the new path. if let Ok(file) = open_result { self.close_entry(); self.name_buffer.populate(path.as_buffer()); self.entry = Some((Entry::File(BufferedFile::new(file)), path)); return Ok(()); }; } else if metadata.is_dir() { // Keep the current entry open if we can't open the new path. if let Some(listing) = path.directory_listing() { self.close_entry(); self.name_buffer.populate(path.as_buffer()); self.entry = Some((Entry::Directory(listing), path)); return Ok(()); }; }; return Err(()); } pub fn write_to_move_port(&mut self, byte: u8) { if let Some(buffer) = self.move_buffer.push_byte(byte) { let blank_destination = buffer[0] == 0x00; let destination = BedrockFilePath::from_buffer(buffer, &self.base_path); self.success = false; if let Some((_, source)) = &self.entry { if blank_destination { if self.enable_delete { self.success = delete_entry(&source.as_path()); } } else if let Some(dest) = destination { if self.enable_move { self.success = move_entry(&source.as_path(), &dest.as_path()); } } } else if let Some(dest) = destination { if self.enable_create { self.success = create_file(&dest.as_path()); } } self.close_entry(); } } /// Attempt to open the parent directory of the current entry. pub fn ascend_to_parent(&mut self) { self.success = false; if let Some((_, path)) = &self.entry { if let Some(parent_path) = path.parent() { self.success = self.open_entry(parent_path).is_ok(); } } else { if let Some(default) = BedrockFilePath::from_path(&self.default_path, &self.base_path) { self.success = self.open_entry(default).is_ok(); } } } /// Attempt to open the currently-selected child of the current directory. pub fn descend_to_child(&mut self) { self.success = false; if let Some((Entry::Directory(listing), _)) = &self.entry { if let Some(child_path) = listing.child_path() { self.success = self.open_entry(child_path).is_ok(); }; } } pub fn set_name_pointer(&mut self, value: u8) { self.name_buffer.set_pointer(value); } /// Returns true if the currently-open entry is a directory. pub fn entry_type(&self) -> bool { match self.entry { Some((Entry::Directory(_), _)) => true, _ => false, } } /// Reads a byte from the name buffer of the currently-selected child. 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); } } /// Returns 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 } } /// Reads a byte from the currently-open file. pub fn read_byte(&mut self) -> u8 { match &mut self.entry { Some((Entry::File(buffered_file), _)) => buffered_file.read_byte(), _ => 0, } } /// Writes a byte to the currently-open file. 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.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.length); match &mut self.entry { Some((Entry::File(buffered_file), _)) => buffered_file.set_length(length), _ => (), } } } impl Drop for FileDevice { fn drop(&mut self) { self.close_entry(); } }