diff options
Diffstat (limited to 'src/devices/file.rs')
| -rw-r--r-- | src/devices/file.rs | 285 | 
1 files changed, 226 insertions, 59 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(), +            base_path: PathBuf::from("/home/ben/Sandbox"), + +            open_buffer: CircularPathBuffer::new(), +            move_buffer: CircularPathBuffer::new(), +            name_buffer: CircularPathBuffer::new(), -            file: None, -            operation_state: false, -            pointer: 0, -            file_size: 0, +            entry: None, + +            move_success: false, +            new_pointer: 0, +            new_length: 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; +    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(()) +        }      }  }  | 
