diff options
author | Ben Bridle <bridle.benjamin@gmail.com> | 2024-01-31 07:38:50 +1300 |
---|---|---|
committer | Ben Bridle <bridle.benjamin@gmail.com> | 2024-01-31 07:39:07 +1300 |
commit | 28101de56231252ca0cfa6a9f107b75112c9acad (patch) | |
tree | f5c82a6894562bfb8ed8ab94e9345cefaa6fb96b | |
parent | 30d2f099c9edf4f59fbbdd6686988ae7b0622ba2 (diff) | |
download | bedrock-pc-28101de56231252ca0cfa6a9f107b75112c9acad.zip |
Implement new file device interface
This is a complete redesign of the file device. The most notable
addition is the ability to ascend and descend the file tree.
-rw-r--r-- | src/devices.rs | 64 | ||||
-rw-r--r-- | src/devices/file.rs | 293 | ||||
-rw-r--r-- | src/devices/file/buffered_file.rs | 127 | ||||
-rw-r--r-- | src/devices/file/circular_path_buffer.rs | 66 | ||||
-rw-r--r-- | src/devices/file/directory_entry.rs | 30 | ||||
-rw-r--r-- | src/devices/file/directory_listing.rs | 108 | ||||
-rw-r--r-- | src/devices/file/entry.rs | 36 | ||||
-rw-r--r-- | src/devices/file/operations.rs | 43 | ||||
-rw-r--r-- | src/main.rs | 2 |
9 files changed, 674 insertions, 95 deletions
diff --git a/src/devices.rs b/src/devices.rs index 5b274c9..e1a00f7 100644 --- a/src/devices.rs +++ b/src/devices.rs @@ -182,22 +182,22 @@ impl DeviceBus for StandardDevices { // Stream // File - 0xA0 => read_b!(self.file.file.is_some()), - 0xA1 => read_b!(self.file.operation_state), - 0xA2 => no_read!(), - 0xA3 => no_read!(), - 0xA4 => read_hh!(self.file.pointer), - 0xA5 => read_hl!(self.file.pointer), - 0xA6 => read_lh!(self.file.pointer), - 0xA7 => read_ll!(self.file.pointer), - 0xA8 => self.file.read(), - 0xA9 => self.file.read(), - 0xAA => no_read!(), - 0xAB => no_read!(), - 0xAC => read_hh!(self.file.file_size), - 0xAD => read_hl!(self.file.file_size), - 0xAE => read_lh!(self.file.file_size), - 0xAF => read_ll!(self.file.file_size), + 0xA0 => read_b!(self.file.entry.is_some()), + 0xA1 => read_b!(self.file.move_success), + 0xA2 => self.file.name_buffer.read_byte(), + 0xA3 => read_b!(self.file.entry_type()), + 0xA4 => self.file.read_byte(), + 0xA5 => self.file.read_byte(), + 0xA6 => self.file.read_child_name(), + 0xA7 => read_b!(self.file.child_type()), + 0xA8 => read_hh!(self.file.pointer()), + 0xA9 => read_hl!(self.file.pointer()), + 0xAA => read_lh!(self.file.pointer()), + 0xAB => read_ll!(self.file.pointer()), + 0xAC => read_hh!(self.file.length()), + 0xAD => read_hl!(self.file.length()), + 0xAE => read_lh!(self.file.length()), + 0xAF => read_ll!(self.file.length()), _ => unimplemented!("Reading from device port 0x{port:02x}"), } @@ -310,22 +310,22 @@ impl DeviceBus for StandardDevices { 0x8E => no_write!(), 0x8F => no_write!(), // File - 0xA0 => self.file.push_name_byte(val), - 0xA1 => no_write!(), - 0xA2 => no_write!(), - 0xA3 => no_write!(), - 0xA4 => write_hh!(self.file.pointer), - 0xA5 => write_hl!(self.file.pointer), - 0xA6 => write_lh!(self.file.pointer), - 0xA7 => write_ll!(self.file.pointer), - 0xA8 => no_write!(), - 0xA9 => no_write!(), - 0xAA => no_write!(), - 0xAB => no_write!(), - 0xAC => write_hh!(self.file.file_size), - 0xAD => write_hl!(self.file.file_size), - 0xAE => write_lh!(self.file.file_size), - 0xAF => { write_ll!(self.file.file_size); self.file.set_file_size() }, + 0xA0 => self.file.write_to_open_port(val), + 0xA1 => self.file.write_to_move_port(val), + 0xA2 => self.file.set_name_pointer(val), + 0xA3 => self.file.ascend_to_parent(), + 0xA4 => self.file.write_byte(val), + 0xA5 => self.file.write_byte(val), + 0xA6 => self.file.set_child_name_pointer(val), + 0xA7 => self.file.descend_to_child(), + 0xA8 => write_hh!(self.file.new_pointer), + 0xA9 => write_hl!(self.file.new_pointer), + 0xAA => write_lh!(self.file.new_pointer), + 0xAB => { write_ll!(self.file.new_pointer); self.file.commit_pointer() }, + 0xAC => write_hh!(self.file.new_length), + 0xAD => write_hl!(self.file.new_length), + 0xAE => write_lh!(self.file.new_length), + 0xAF => { write_ll!(self.file.new_length); self.file.commit_length() }, // Bytestreams 0x96 => self.stream.write_stdout(val), 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(()) + } } } diff --git a/src/devices/file/buffered_file.rs b/src/devices/file/buffered_file.rs new file mode 100644 index 0000000..091b5d9 --- /dev/null +++ b/src/devices/file/buffered_file.rs @@ -0,0 +1,127 @@ +use std::fs::File; +use std::io::{BufReader, BufWriter}; +use std::io::{Read, Write}; +use std::io::{ErrorKind, Seek, SeekFrom}; + +enum AccessMode { + Read(BufReader<File>), + Write(BufWriter<File>), + None, +} + +impl AccessMode { + pub fn unwrap(self) -> File { + match self { + Self::Read(reader) => reader.into_inner(), + Self::Write(writer) => writer.into_inner().unwrap(), + Self::None => unreachable!(), + } + } +} + +impl Default for AccessMode { + fn default() -> Self { + Self::None + } +} + +pub struct BufferedFile { + file: AccessMode, +} + +impl BufferedFile { + pub fn new(file: File) -> Self { + Self { + file: AccessMode::Read(BufReader::new(file)), + } + } + + pub fn read_byte(&mut self) -> u8 { + let mut buffer = [0u8; 1]; + + let read_result = match &mut self.file { + AccessMode::Read(reader) => reader.read_exact(&mut buffer), + AccessMode::Write(writer) => { + let address = writer.stream_position().unwrap(); + let file = std::mem::take(&mut self.file).unwrap(); + let mut reader = BufReader::new(file); + reader.seek(SeekFrom::Start(address)).unwrap(); + let read_result = reader.read_exact(&mut buffer); + self.file = AccessMode::Read(reader); + read_result + } + AccessMode::None => unreachable!(), + }; + + match read_result { + Ok(_) => buffer[0], + Err(error) => match error.kind() { + ErrorKind::UnexpectedEof => 0, + _ => panic!("{error:?}"), + } + } + } + + pub fn write_byte(&mut self, byte: u8) { + let mut buffer = [byte; 1]; + + let write_result = match &mut self.file { + AccessMode::Write(writer) => writer.write_all(&mut buffer), + AccessMode::Read(reader) => { + let address = reader.stream_position().unwrap(); + let file = std::mem::take(&mut self.file).unwrap(); + let mut writer = BufWriter::new(file); + writer.seek(SeekFrom::Start(address)).unwrap(); + let write_result = writer.write_all(&mut buffer); + self.file = AccessMode::Write(writer); + write_result + } + AccessMode::None => unreachable!(), + }; + + write_result.unwrap(); + } + + pub fn pointer(&mut self) -> u32 { + let position = match &mut self.file { + AccessMode::Read(reader) => reader.stream_position(), + AccessMode::Write(writer) => writer.stream_position(), + AccessMode::None => unreachable!(), + }; + u32::try_from(position.unwrap()).unwrap_or(u32::MAX) + } + + pub fn set_pointer(&mut self, pointer: u32) { + let position = SeekFrom::Start(pointer as u64); + match &mut self.file { + AccessMode::Read(reader) => reader.seek(position).unwrap(), + AccessMode::Write(writer) => writer.seek(position).unwrap(), + AccessMode::None => unreachable!(), + }; + } + + pub fn length(&mut self) -> u32 { + let length = match &mut self.file { + AccessMode::Read(reader) => reader.stream_len(), + AccessMode::Write(writer) => writer.stream_len(), + AccessMode::None => unreachable!(), + }; + u32::try_from(length.unwrap()).unwrap_or(u32::MAX) + } + + pub fn set_length(&mut self, length: u32) { + match &mut self.file { + AccessMode::Read(_) => { + let file = std::mem::take(&mut self.file).unwrap(); + file.set_len(length as u64).unwrap(); + self.file = AccessMode::Read(BufReader::new(file)); + } + AccessMode::Write(_) => { + let file = std::mem::take(&mut self.file).unwrap(); + file.set_len(length as u64).unwrap(); + self.file = AccessMode::Read(BufReader::new(file)); + } + AccessMode::None => unreachable!(), + }; + } +} diff --git a/src/devices/file/circular_path_buffer.rs b/src/devices/file/circular_path_buffer.rs new file mode 100644 index 0000000..f828f73 --- /dev/null +++ b/src/devices/file/circular_path_buffer.rs @@ -0,0 +1,66 @@ +use std::ffi::OsString; +use std::os::unix::ffi::OsStringExt; +use std::path::PathBuf; + +pub struct CircularPathBuffer { + buffer: [u8; 256], + pointer: u8, +} + +impl CircularPathBuffer { + pub fn new() -> Self { + Self { buffer: [0; 256] , pointer: 0 } + } + + pub fn clear(&mut self) { + self.buffer.fill(0); + self.pointer = 0; + } + + pub fn populate(&mut self, bytes: &[u8]) { + self.clear(); + if bytes.len() > 255 { + unreachable!( + "Attempted to populate CircularPathBuffer with {} bytes: {:?}", + bytes.len(), String::from_utf8_lossy(bytes) + ) + } + let self_slice = &mut self.buffer[..bytes.len()]; + self_slice.copy_from_slice(&bytes); + } + + pub fn set_pointer(&mut self, value: u8) { + self.pointer = 0; + if value != 0x00 { + for (i, c) in self.buffer.iter().enumerate() { + match c { + b'/' => self.pointer = (i + 1) as u8, + 0x00 => break, + _ => continue, + } + } + } + } + + pub fn read_byte(&mut self) -> u8 { + let pointer = self.pointer as usize; + self.pointer = self.pointer.wrapping_add(1); + self.buffer[pointer] + } + + // Returns an unsanitized relative path. + pub fn push_byte(&mut self, value: u8) -> Option<PathBuf> { + if value == 0x00 { + let pointer = self.pointer as usize; + let vec = self.buffer[..pointer].to_vec(); + self.clear(); + let os_string: OsString = OsStringExt::from_vec(vec); + Some(os_string.into()) + } else { + let pointer = self.pointer as usize; + self.pointer = self.pointer.wrapping_add(1); + self.buffer[pointer] = value; + None + } + } +} diff --git a/src/devices/file/directory_entry.rs b/src/devices/file/directory_entry.rs new file mode 100644 index 0000000..45de817 --- /dev/null +++ b/src/devices/file/directory_entry.rs @@ -0,0 +1,30 @@ +use crate::*; + +use std::cmp::Ordering; + +#[derive(PartialEq, Eq)] +pub struct DirectoryChild { + pub byte_path: Vec<u8>, + pub entry_type: EntryType, +} + +impl PartialOrd for DirectoryChild { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + let entry_type_ord = self.entry_type.cmp(&other.entry_type); + let final_order = match entry_type_ord { + Ordering::Equal => self.byte_path.cmp(&other.byte_path), + _ => entry_type_ord, + }; + Some(final_order) + } +} + +impl Ord for DirectoryChild { + fn cmp(&self, other: &Self) -> Ordering { + let entry_type_ord = self.entry_type.cmp(&other.entry_type); + match entry_type_ord { + Ordering::Equal => self.byte_path.cmp(&other.byte_path), + _ => entry_type_ord, + } + } +} diff --git a/src/devices/file/directory_listing.rs b/src/devices/file/directory_listing.rs new file mode 100644 index 0000000..1f94a3a --- /dev/null +++ b/src/devices/file/directory_listing.rs @@ -0,0 +1,108 @@ +use crate::*; + +use std::ffi::OsString; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; +use std::path::{Component, Path, PathBuf}; + + +pub struct DirectoryListing { + children: Vec<DirectoryChild>, + length: u32, + selected: u32, + name_buffer: CircularPathBuffer, +} + +impl DirectoryListing { + pub fn from_path(path: &Path, base: &Path) -> Result<Self, ()> { + macro_rules! continue_on_err { + ($result:expr) => { match $result { Ok(v) => v, Err(_) => continue} }; + } + + let mut children = Vec::new(); + if let Ok(iter_dir) = std::fs::read_dir(path) { + for (i, entry_result) in iter_dir.enumerate() { + if i == u16::MAX as usize { + println!("Warning, this directory contains more than 65536 entries.") + }; + let entry = continue_on_err!(entry_result); + let path = continue_on_err!(remove_base(&entry.path(), &base)); + let byte_path = path.as_os_str().as_bytes(); + if byte_path.len() > 255 { + continue; + }; + let metadata = continue_on_err!(std::fs::metadata(&entry.path())); + children.push( DirectoryChild { + byte_path: Vec::from(byte_path), + entry_type: if metadata.is_file() { + EntryType::File + } else if metadata.is_dir() { + EntryType::Directory + } else { + unreachable!(); + }, + } ) + } + children.sort_unstable(); + let length = u32::try_from(children.len()).unwrap_or(u32::MAX); + let selected = 0; + let name_buffer = CircularPathBuffer::new(); + Ok(Self { children, length, selected, name_buffer } ) + } else { + Err(()) + } + } + + pub fn get(&self, index: u32) -> Option<&DirectoryChild> { + self.children.get(index as usize) + } + + pub fn length(&self) -> u32 { + self.length + } + + pub fn selected(&self) -> u32 { + self.selected + } + + pub fn set_selected(&mut self, index: u32) { + if let Some(info) = self.get(index) { + self.name_buffer.populate(&info.byte_path.clone()); + self.selected = index; + } else { + self.name_buffer.clear(); + self.selected = 0; + } + } + + pub fn child_name(&mut self) -> &mut CircularPathBuffer { + &mut self.name_buffer + } + + pub fn child_type(&self) -> Option<EntryType> { + self.get(self.selected).and_then(|i| Some(i.entry_type)) + } + + pub fn child_path(&self) -> Option<PathBuf> { + self.get(self.selected).and_then(|i| { + let os_string: OsString = OsStringExt::from_vec(i.byte_path.clone()); + Some(os_string.into()) + }) + } +} + +pub fn remove_base(absolute_path: &Path, base_path: &Path) -> Result<PathBuf, ()> { + if let Ok(relative) = absolute_path.strip_prefix(base_path) { + let mut baseless_path = PathBuf::from("/"); + for component in relative.components() { + match component { + Component::Normal(s) => baseless_path.push(s), + Component::ParentDir => return Err(()), + Component::CurDir => continue, + Component::RootDir => continue, + Component::Prefix(_) => continue, + } + } + return Ok(baseless_path); + } + return Err(()); +} diff --git a/src/devices/file/entry.rs b/src/devices/file/entry.rs new file mode 100644 index 0000000..a91ae82 --- /dev/null +++ b/src/devices/file/entry.rs @@ -0,0 +1,36 @@ +use crate::*; + +use std::cmp::Ordering; + +pub enum Entry { + File(BufferedFile), + Directory(DirectoryListing), +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum EntryType { + File, + Directory, +} + +impl PartialOrd for EntryType { + fn partial_cmp(&self, other: &Self) -> Option<Ordering> { + match (self, other) { + (EntryType::Directory, EntryType::Directory) => Some(Ordering::Equal ), + (EntryType::Directory, EntryType::File ) => Some(Ordering::Less ), + (EntryType::File, EntryType::Directory) => Some(Ordering::Greater), + (EntryType::File, EntryType::File ) => Some(Ordering::Equal ), + } + } +} + +impl Ord for EntryType { + fn cmp(&self, other: &Self) -> Ordering { + match (self, other) { + (EntryType::Directory, EntryType::Directory) => Ordering::Equal , + (EntryType::Directory, EntryType::File ) => Ordering::Less , + (EntryType::File, EntryType::Directory) => Ordering::Greater, + (EntryType::File, EntryType::File ) => Ordering::Equal , + } + } +} diff --git a/src/devices/file/operations.rs b/src/devices/file/operations.rs new file mode 100644 index 0000000..f33509b --- /dev/null +++ b/src/devices/file/operations.rs @@ -0,0 +1,43 @@ +use std::io::ErrorKind; +use std::path::Path; + +pub fn entry_exists(source: &Path) -> bool { + std::fs::metadata(source).is_ok() +} + +// Delete a file or folder, returning true if successful. +pub fn delete_entry(source: &Path) -> bool { + match std::fs::remove_file(source) { + Ok(_) => true, + Err(e) => match e.kind() { + ErrorKind::NotFound => true, + ErrorKind::IsADirectory => match std::fs::remove_dir_all(source) { + Ok(_) => true, + Err(e) => match e.kind() { + ErrorKind::NotFound => true, + _ => false, + } + } + _ => false, + } + } +} + +pub fn move_entry(source: &Path, destination: &Path) -> bool { + if !entry_exists(source) || entry_exists(destination) { + return false; + } + std::fs::rename(source, destination).is_ok() +} + +pub fn create_file(destination: &Path) -> bool { + if entry_exists(destination) { + false + } else { + if let Some(parent_path) = destination.parent() { + let _ = std::fs::create_dir_all(parent_path); + } + std::fs::OpenOptions::new().write(true).create_new(true) + .open(destination).is_ok() + } +} diff --git a/src/main.rs b/src/main.rs index 98bc20f..80dcb54 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ #![feature(bigint_helper_methods)] +#![feature(io_error_more)] #![feature(split_array)] +#![feature(seek_stream_len)] use std::io::Read; use std::process::exit; |