use super::*; pub struct DirectoryListing { children: Vec, length: u32, selected: Option, name_buffer: CircularPathBuffer, } impl DirectoryListing { pub fn from_path(path: &BedrockFilePath) -> Option { macro_rules! unres { ($result:expr) => { match $result { Ok(v) => v, Err(_) => continue} }; } macro_rules! unopt { ($option:expr) => { match $option { Some(v) => v, None => continue} }; } let mut children = Vec::new(); let dir_listing = std::fs::read_dir(path.as_path()).ok()?; for (i, entry_result) in dir_listing.enumerate() { // Firebreak to prevent emulator from consuming an absurd amount // of memory when opening too large of a directory. if i == (u16::MAX as usize) { break; } let entry = unres!(entry_result); let entry_path = unopt!(BedrockFilePath::from_path(&entry.path(), path.base())); if entry_path.filename_is_dot_prefixed() { continue; } let metadata = unres!(std::fs::metadata(&entry.path())); let entry_type = if metadata.is_file() { EntryType::File } else if metadata.is_dir() { EntryType::Directory } else { continue; }; let child = DirectoryChild { path: entry_path, entry_type }; children.push(child); } children.sort(); let length = u32::try_from(children.len()).ok()?; let selected = None; let name_buffer = CircularPathBuffer::new(); Some( Self { children, length, selected, name_buffer } ) } /// Attempts to return a directory child by index. pub fn get(&self, index: u32) -> Option<&DirectoryChild> { self.children.get(index as usize) } pub fn length(&self) -> u32 { self.length } /// Returns the index of the selected child, or zero if no child is selected. pub fn selected(&self) -> u32 { self.selected.unwrap_or(0) } /// Attempts to select a child by index. pub fn set_selected(&mut self, index: u32) { if let Some(child) = self.get(index) { let buffer = child.path.as_buffer(); self.name_buffer.populate(buffer); self.selected = Some(index); } else { self.name_buffer.clear(); self.selected = None; } } pub fn child_name(&mut self) -> &mut CircularPathBuffer { &mut self.name_buffer } pub fn child_type(&self) -> Option { self.selected.and_then(|s| self.get(s).and_then(|i| Some(i.entry_type))) } pub fn child_path(&self) -> Option { self.selected.and_then(|s| self.get(s).and_then(|i| Some(i.path.clone()))) } }