diff options
Diffstat (limited to 'src/devices/file.rs')
-rw-r--r-- | src/devices/file.rs | 293 |
1 files changed, 230 insertions, 63 deletions
diff --git a/src/devices/file.rs b/src/devices/file.rs index f442dd8..163c10d 100644 --- a/src/devices/file.rs +++ b/src/devices/file.rs @@ -1,90 +1,257 @@ -use std::io::{ErrorKind, Read, Seek, SeekFrom}; -use std::fs::{File, OpenOptions}; +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 { - pub name: Vec<u8>, - pub rename: Vec<u8>, + /// The path to which the file device is confined. Files and directories + /// outside of this directory cannot be accessed. + pub base_path: PathBuf, - pub file: Option<File>, - pub operation_state: bool, - pub pointer: u32, - pub file_size: u32, + 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 { - name: Vec::new(), - rename: Vec::new(), - - file: None, - operation_state: false, - pointer: 0, - file_size: 0, - } - } - - pub fn push_name_byte(&mut self, byte: u8) { - if byte != 0 { self.name.push(byte); return } - // If char was null, attempt to open the file and read file size. - let path: std::ffi::OsString = std::os::unix::ffi::OsStringExt::from_vec( - std::mem::take(&mut self.name)); - let try_open = OpenOptions::new().read(true).write(true).open(path); - if let Ok(file) = try_open { - if let Ok(metadata) = file.metadata() { - // Success. - self.file = Some(file); - match u32::try_from(metadata.len()) { - Ok(len) => self.file_size = len, - Err(_) => self.file_size = u32::MAX, - } - return; + 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 close_entry(&mut self) { + self.open_buffer.clear(); + self.move_buffer.clear(); + self.name_buffer.clear(); + 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) { + println!("Attempting to open path {relative_path:?}"); + self.close_entry(); + if !is_blank_path(&relative_path) { + if let Ok(path) = self.attach_base(&relative_path) { + println!("Attempting to open absolute path {path:?}"); + let _ = self.open_entry(&path); + }; } } - // Failure. - self.close(); } - pub fn set_file_size(&mut self) { - if let Some(file) = &self.file { - if let Ok(()) = file.set_len(self.file_size.into()) { - return; + 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() { + println!("Opening file as readable/writable"); + 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()); + println!("Success, opened file {path:?}"); + return Ok(()); }; - } - self.close(); + } else if metadata.is_dir() { + println!("Opening directory"); + 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()); + println!("Success, opened directory {path:?}"); + return Ok(()); + }; + }; + return Err(()); } - pub fn read(&mut self) -> u8 { - if let Some(file) = &mut self.file { - let mut buffer: [u8; 1] = [0; 1]; - match file.read_exact(&mut buffer) { - Ok(()) => return buffer[0], - Err(err) => match err.kind() { - ErrorKind::UnexpectedEof => return 0, - _ => unimplemented!("File read error: ErrorKind::{:?}", err.kind()), + 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) { + self.move_success = delete_entry(&source); + } else if let Ok(destination) = self.attach_base(&dest) { + println!("Attempting to move entry: {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) { + println!("Attempting to create entry: {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); + } } - self.close(); - return 0; } - pub fn seek(&mut self) { - if let Some(file) = &mut self.file { - let point = SeekFrom::Start(self.pointer.into()); - if let Ok(pointer) = file.seek(point) { - if let Ok(pointer_u32) = pointer.try_into() { - self.pointer = pointer_u32; - return; + /// 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); } }; } - self.close(); } - pub fn close(&mut self) { - self.file = None; - self.pointer = 0; - self.file_size = 0; + /// 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(()) + } } } |