use crate::*; use std::ffi::OsString; use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::path::{Component, Path, PathBuf}; pub struct DirectoryListing { children: Vec, length: u32, selected: u32, name_buffer: CircularPathBuffer, } impl DirectoryListing { pub fn from_path(path: &Path, base: &Path) -> Result { 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 { self.get(self.selected).and_then(|i| Some(i.entry_type)) } pub fn child_path(&self) -> Option { 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 { 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(()); }