use std::path::{Path, PathBuf};
use std::sync::Arc;
use iced::Task;
use swdir::DirEntry;
use super::executor::{ScanExecutor, run_scan};
use super::message::{DirectoryTreeEvent, LoadPayload};
use super::node::LoadedEntry;
use crate::Error;
pub(crate) fn scan(
executor: Arc<dyn ScanExecutor>,
path: PathBuf,
generation: u64,
depth: u32,
) -> Task<DirectoryTreeEvent> {
let target = path.clone();
let fut = run_scan(&executor, path);
Task::perform(
async move {
let raw = fut.await;
raw.as_ref()
.map(|entries| normalize_entries(entries))
.map_err(Error::from)
},
move |result| {
DirectoryTreeEvent::Loaded(LoadPayload {
path: target.clone(),
generation,
depth,
result: Arc::new(result),
})
},
)
}
pub(crate) fn normalize_entries(entries: &[DirEntry]) -> Vec<LoadedEntry> {
let mut out = Vec::with_capacity(entries.len());
for e in entries {
let path = e.path().to_path_buf();
let is_dir = e.is_dir();
let is_symlink = e.is_symlink();
let is_hidden = is_hidden(&path, e);
out.push(LoadedEntry {
path,
is_dir,
is_symlink,
is_hidden,
});
}
out.sort_by(|a, b| match (a.is_dir, b.is_dir) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
_ => a
.path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase()
.cmp(
&b.path
.file_name()
.unwrap_or_default()
.to_string_lossy()
.to_lowercase(),
),
});
out
}
#[cfg(unix)]
fn is_hidden(_path: &Path, entry: &DirEntry) -> bool {
entry
.path()
.file_name()
.map(|n| {
let s = n.to_string_lossy();
s.starts_with('.') && s.as_ref() != "." && s.as_ref() != ".."
})
.unwrap_or(false)
}
#[cfg(windows)]
fn is_hidden(_path: &Path, entry: &DirEntry) -> bool {
use std::os::windows::fs::MetadataExt;
const HIDDEN_ATTR: u32 = 0x2;
let hidden_bit = entry
.metadata()
.map(|m| m.file_attributes() & HIDDEN_ATTR != 0)
.unwrap_or(false);
let dotfile = entry
.path()
.file_name()
.map(|n| n.to_string_lossy().starts_with('.'))
.unwrap_or(false);
hidden_bit || dotfile
}
#[cfg(not(any(unix, windows)))]
fn is_hidden(_path: &Path, entry: &DirEntry) -> bool {
entry
.path()
.file_name()
.map(|n| n.to_string_lossy().starts_with('.'))
.unwrap_or(false)
}
#[cfg(test)]
mod tests;