diff options
Diffstat (limited to 'src/devices/file.rs')
-rw-r--r-- | src/devices/file.rs | 201 |
1 files changed, 82 insertions, 119 deletions
diff --git a/src/devices/file.rs b/src/devices/file.rs index d53db0b..26e14da 100644 --- a/src/devices/file.rs +++ b/src/devices/file.rs @@ -1,42 +1,23 @@ +mod bedrock_file_path; mod buffered_file; mod circular_path_buffer; -mod directory_entry; +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_entry::*; +pub use directory_child::*; pub use directory_listing::*; pub use entry::*; use operations::*; -#[cfg(target_family = "unix")] -use std::os::unix::ffi::OsStrExt; -use std::fs::{OpenOptions, metadata}; use std::path::{Component, Path, PathBuf}; -fn is_blank_path(path: &Path) -> bool { - path == PathBuf::new() -} - -fn bytes_to_path(bytes: &[u8]) -> PathBuf { - #[cfg(target_family = "unix")] - let os_string: std::ffi::OsString = { - std::os::unix::ffi::OsStringExt::from_vec(Vec::from(bytes)) - }; - #[cfg(target_family = "windows")] - let os_string: std::ffi::OsString = { - let wide: Vec<u16> = bytes.iter().map(|b| *b as u16).collect(); - std::os::windows::ffi::OsStringExt::from_wide(&wide) - }; - os_string.into() -} 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 default_path: PathBuf, @@ -44,108 +25,104 @@ pub struct FileDevice { pub move_buffer: CircularPathBuffer, pub name_buffer: CircularPathBuffer, - pub entry: Option<(Entry, PathBuf)>, + pub entry: Option<(Entry, BedrockFilePath)>, - pub op_success: bool, - pub new_pointer: u32, - pub new_length: u32, + 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, - pub enable_read: bool, - pub enable_write: 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: PathBuf::from("/"), + base_path: default_base, default_path: match std::env::current_dir() { Ok(dir) => PathBuf::from(dir), - Err(_) => PathBuf::from("/"), + Err(_) => PathBuf::from(""), }, + open_buffer: CircularPathBuffer::new(), move_buffer: CircularPathBuffer::new(), name_buffer: CircularPathBuffer::new(), entry: None, - op_success: false, - new_pointer: 0, - new_length: 0, + success: false, + pointer: 0, + length: 0, + enable_read: true, + enable_write: true, enable_create: true, enable_move: true, enable_delete: false, - enable_read: true, - enable_write: true, } } + /// 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.new_pointer = 0; - self.new_length = 0; + 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(relative_path) = self.open_buffer.push_byte(byte) { + if let Some(buffer) = self.open_buffer.push_byte(byte) { self.close_entry(); - if !is_blank_path(&relative_path) { - if let Ok(path) = self.attach_base(&relative_path) { - let _ = self.open_entry(&path); - }; + if let Some(path) = BedrockFilePath::from_buffer(buffer, &self.base_path) { + self.success = self.open_entry(path).is_ok(); } } } - 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)); + /// 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 = OpenOptions::new() + let open_result = std::fs::OpenOptions::new() .read(self.enable_read) .write(self.enable_write) - .open(&path); + .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(); - 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(); - #[cfg(target_family = "unix")] - self.name_buffer.populate(relative.as_os_str().as_bytes()); - #[cfg(target_family = "windows")] - self.name_buffer.populate(relative.as_os_str().as_encoded_bytes()); + self.name_buffer.populate(path.as_buffer()); + self.entry = Some((Entry::File(BufferedFile::new(file)), path)); 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(); - #[cfg(target_family = "unix")] - self.name_buffer.populate(relative.as_os_str().as_bytes()); - #[cfg(target_family = "windows")] - self.name_buffer.populate(relative.as_os_str().as_encoded_bytes()); + // 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(()); }; }; @@ -153,58 +130,60 @@ impl FileDevice { } pub fn write_to_move_port(&mut self, byte: u8) { - if let Some(dest) = self.move_buffer.push_byte(byte) { + 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 is_blank_path(&dest) { - match self.enable_delete { - true => self.op_success = delete_entry(&source), - false => self.op_success = false, + if blank_destination { + if self.enable_delete { + self.success = delete_entry(&source.as_path()); } - } else if let Ok(destination) = self.attach_base(&dest) { - match self.enable_move { - true => self.op_success = move_entry(&source, &destination), - false => self.op_success = false, + } else if let Some(dest) = destination { + if self.enable_move { + self.success = move_entry(&source.as_path(), &dest.as_path()); } } - } else { - if is_blank_path(&dest) { - self.op_success = false; - } else if let Ok(destination) = self.attach_base(&dest) { - match self.enable_create { - true => self.op_success = create_file(&destination), - false => self.op_success = false, - } + } 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.op_success = self.open_entry(&parent_path.to_owned()).is_ok(); - } else { - self.op_success = false; + self.success = self.open_entry(parent_path).is_ok(); } } else { - self.op_success = self.open_entry(&self.default_path.to_owned()).is_ok(); + 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. + /// 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() { - if let Ok(child_path) = self.attach_base(&child_path) { - self.op_success = self.open_entry(&child_path).is_ok(); - } + self.success = self.open_entry(child_path).is_ok(); }; } } - /// Return true if the currently-open entry is a directory. + 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, @@ -212,7 +191,7 @@ impl FileDevice { } } - /// Return true if the currently-selected child is a directory. + /// 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() @@ -227,7 +206,7 @@ impl FileDevice { } } - /// Return true if the currently-selected child is a directory. + /// 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() { @@ -240,6 +219,7 @@ impl FileDevice { } } + /// 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(), @@ -247,6 +227,7 @@ impl FileDevice { } } + /// 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), @@ -263,7 +244,7 @@ impl FileDevice { } pub fn commit_pointer(&mut self) { - let pointer = std::mem::take(&mut self.new_pointer); + 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), @@ -280,30 +261,12 @@ impl FileDevice { } pub fn commit_length(&mut self) { - let length = std::mem::take(&mut self.new_length); + let length = std::mem::take(&mut self.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 { |