diff options
Diffstat (limited to 'src/devices/file/directory_listing.rs')
-rw-r--r-- | src/devices/file/directory_listing.rs | 108 |
1 files changed, 108 insertions, 0 deletions
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(()); +} |