summaryrefslogtreecommitdiff
path: root/src/devices/file/directory_listing.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/file/directory_listing.rs')
-rw-r--r--src/devices/file/directory_listing.rs108
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(());
+}