summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrabite <rabite@posteo.de>2019-03-25 02:53:59 +0100
committerrabite <rabite@posteo.de>2019-03-25 02:53:59 +0100
commit3888f49aebb911ac6b2d83f9f78f2feb092069a8 (patch)
treed33a0fe09dd7fd31e6daebef4ec8559f292026ab
parentfd366a26dcddfc50d54ec0481a8f6e4848356b32 (diff)
load metadata and file-count asynchronously
-rw-r--r--src/dirty.rs63
-rw-r--r--src/fail.rs86
-rw-r--r--src/file_browser.rs13
-rw-r--r--src/files.rs359
-rw-r--r--src/listview.rs36
-rw-r--r--src/preview.rs104
-rw-r--r--src/widget.rs12
7 files changed, 480 insertions, 193 deletions
diff --git a/src/dirty.rs b/src/dirty.rs
index fe6e4d1..14c6245 100644
--- a/src/dirty.rs
+++ b/src/dirty.rs
@@ -1,22 +1,38 @@
+use std::sync::{Arc, RwLock};
+use std::hash::{Hash, Hasher};
+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct DirtyBit(bool);
-pub trait Dirtyable {
- fn get_bit(&self) -> &DirtyBit;
- fn get_bit_mut(&mut self) -> &mut DirtyBit;
+#[derive(Debug)]
+pub struct AsyncDirtyBit(pub Arc<RwLock<DirtyBit>>);
- fn is_dirty(&self) -> bool {
- self.get_bit().0
+impl PartialEq for AsyncDirtyBit {
+ fn eq(&self, other: &AsyncDirtyBit) -> bool {
+ *self.0.read().unwrap() == *other.0.read().unwrap()
}
+}
- fn set_dirty(&mut self) {
- self.get_bit_mut().0 = true;
+impl Eq for AsyncDirtyBit {}
+
+impl Hash for AsyncDirtyBit {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.0.read().unwrap().hash(state)
}
- fn set_clean(&mut self) {
- self.get_bit_mut().0 = false;
+}
+
+impl Clone for AsyncDirtyBit {
+ fn clone(&self) -> Self {
+ AsyncDirtyBit(self.0.clone())
}
}
+pub trait Dirtyable {
+ fn is_dirty(&self) -> bool;
+ fn set_dirty(&mut self);
+ fn set_clean(&mut self);
+}
+
impl DirtyBit {
pub fn new() -> DirtyBit {
@@ -24,12 +40,33 @@ impl DirtyBit {
}
}
+impl AsyncDirtyBit {
+ pub fn new() -> AsyncDirtyBit {
+ AsyncDirtyBit(Arc::new(RwLock::new(DirtyBit::new())))
+ }
+}
+
impl Dirtyable for DirtyBit {
- fn get_bit(&self) -> &DirtyBit {
- self
+ fn is_dirty(&self) -> bool {
+ self.0
+ }
+ fn set_dirty(&mut self) {
+ self.0 = true;
}
- fn get_bit_mut(&mut self) -> &mut DirtyBit {
- self
+ fn set_clean(&mut self) {
+ self.0 = false;
+ }
+}
+
+impl Dirtyable for AsyncDirtyBit {
+ fn is_dirty(&self) -> bool {
+ self.0.read().unwrap().is_dirty()
+ }
+ fn set_dirty(&mut self) {
+ self.0.write().unwrap().set_dirty();
+ }
+ fn set_clean(&mut self) {
+ self.0.write().unwrap().set_clean();
}
}
diff --git a/src/fail.rs b/src/fail.rs
index 039002a..55cd5aa 100644
--- a/src/fail.rs
+++ b/src/fail.rs
@@ -11,42 +11,44 @@ use crate::foldview::LogEntry;
pub type HResult<T> = Result<T, HError>;
-#[derive(Fail, Debug)]
+#[derive(Fail, Debug, Clone)]
pub enum HError {
- #[fail(display = "IO error: {} ", error)]
- IoError{#[cause] error: std::io::Error, backtrace: Backtrace},
+ #[fail(display = "IO error: {} ", _0)]
+ IoError(String),
#[fail(display = "Mutex failed")]
- MutexError(Backtrace),
+ MutexError,
#[fail(display = "Can't lock!")]
- TryLockError(Backtrace),
+ TryLockError,
#[fail(display = "Channel failed: {}", error)]
ChannelTryRecvError{#[cause] error: std::sync::mpsc::TryRecvError},
#[fail(display = "Channel failed: {}", error)]
ChannelRecvError{#[cause] error: std::sync::mpsc::RecvError},
#[fail(display = "Channel failed")]
- ChannelSendError(Backtrace),
+ ChannelSendError,
#[fail(display = "Previewer failed on file: {}", file)]
- PreviewFailed{file: String, backtrace: Backtrace},
+ PreviewFailed{file: String},
#[fail(display = "StalePreviewer for file: {}", file)]
StalePreviewError{file: String},
#[fail(display = "Accessed stale value")]
- StaleError(Backtrace),
- #[fail(display = "Failed: {}", error)]
- Error{#[cause] error: failure::Error , backtrace: Backtrace},
+ StaleError,
+ #[fail(display = "Failed: {}", _0)]
+ Error(String),
#[fail(display = "Was None!")]
- NoneError(Backtrace),
+ NoneError,
#[fail(display = "Not ready yet!")]
- WillBeNotReady(Backtrace),
+ WillBeNotReady,
#[fail(display = "Not ready yet!")]
- AsyncNotReadyError(Backtrace),
+ AsyncNotReadyError,
+ #[fail(display = "Async is stale!")]
+ AsyncStaleError,
#[fail(display = "Value has already been taken!")]
- AsyncAlreadyTakenError(Backtrace),
+ AsyncAlreadyTakenError,
#[fail(display = "Async Error: {}", _0)]
AsyncError(String),
#[fail(display = "No widget found")]
- NoWidgetError(Backtrace),
+ NoWidgetError,
#[fail(display = "Path: {:?} not in this directory: {:?}", path, dir)]
- WrongDirectoryError{ path: PathBuf, dir: PathBuf , backtrace: Backtrace},
+ WrongDirectoryError{ path: PathBuf, dir: PathBuf},
#[fail(display = "Widget finnished")]
PopupFinnished,
#[fail(display = "No completions found")]
@@ -54,7 +56,7 @@ pub enum HError {
#[fail(display = "No more history")]
NoHistoryError,
#[fail(display = "No core for widget")]
- NoWidgetCoreError(Backtrace),
+ NoWidgetCoreError,
#[fail(display = "No header for widget")]
NoHeaderError,
#[fail(display = "You wanted this!")]
@@ -62,11 +64,11 @@ pub enum HError {
#[fail(display = "HBox ratio mismatch: {} widgets, ratio is {:?}", wnum, ratio)]
HBoxWrongRatioError{ wnum: usize, ratio: Vec<usize> },
#[fail(display = "Got wrong widget: {}! Wanted: {}", got, wanted)]
- WrongWidgetError{got: String, wanted: String, backtrace: Backtrace},
+ WrongWidgetError{got: String, wanted: String},
#[fail(display = "Strip Prefix Error: {}", error)]
- StripPrefixError{#[cause] error: std::path::StripPrefixError, backtrace: Backtrace},
- #[fail(display = "INofify failed: {}", error)]
- INotifyError{#[cause] error: notify::Error, backtrace: Backtrace},
+ StripPrefixError{#[cause] error: std::path::StripPrefixError},
+ #[fail(display = "INofify failed: {}", _0)]
+ INotifyError(String),
#[fail(display = "Tags not loaded yet")]
TagsNotLoadedYetError,
#[fail(display = "Input cancelled!")]
@@ -79,6 +81,8 @@ pub enum HError {
TerminalResizedError,
#[fail(display = "{}", _0)]
Log(String),
+ #[fail(display = "Metadata already processed")]
+ MetadataProcessedError
}
impl HError {
@@ -92,12 +96,12 @@ impl HError {
Err(HError::HBoxWrongRatioError{ wnum: wnum, ratio: ratio })
}
pub fn no_widget<T>() -> HResult<T> {
- Err(HError::NoWidgetError(Backtrace::new()))
+ Err(HError::NoWidgetError)
}
pub fn wrong_widget<T>(got: &str, wanted: &str) -> HResult<T> {
Err(HError::WrongWidgetError{ got: got.to_string(),
- wanted: wanted.to_string(),
- backtrace: Backtrace::new()})
+ wanted: wanted.to_string() })
+
}
pub fn popup_finnished<T>() -> HResult<T> {
Err(HError::PopupFinnished)
@@ -116,13 +120,13 @@ impl HError {
}
pub fn wrong_directory<T>(path: PathBuf, dir: PathBuf) -> HResult<T> {
Err(HError::WrongDirectoryError{ path: path,
- dir: dir,
- backtrace: Backtrace::new() })
+ dir: dir })
+
}
pub fn preview_failed<T>(file: &crate::files::File) -> HResult<T> {
let name = file.name.clone();
- Err(HError::PreviewFailed{ file: name,
- backtrace: Backtrace::new() })
+ Err(HError::PreviewFailed{ file: name })
+
}
pub fn terminal_resized<T>() -> HResult<T> {
@@ -130,20 +134,24 @@ impl HError {
}
pub fn stale<T>() -> HResult<T> {
- Err(HError::StaleError(Backtrace::new()))
+ Err(HError::StaleError)
}
pub fn async_not_ready<T>() -> HResult<T> {
- Err(HError::AsyncNotReadyError(Backtrace::new()))
+ Err(HError::AsyncNotReadyError)
}
pub fn async_taken<T>() -> HResult<T> {
- Err(HError::AsyncAlreadyTakenError(Backtrace::new()))
+ Err(HError::AsyncAlreadyTakenError)
}
pub fn async_error<T>(error: &HError) -> HResult<T> {
Err(HError::AsyncError(format!("{}", error)))
}
+
+ pub fn metadata_processed<T>() -> HResult<T> {
+ Err(HError::MetadataProcessedError)
+ }
}
@@ -195,7 +203,7 @@ impl<T> ErrorLog for HResult<T> {
impl From<std::io::Error> for HError {
fn from(error: std::io::Error) -> Self {
// dbg!(&error);
- let err = HError::IoError { error: error, backtrace: Backtrace::new() };
+ let err = HError::IoError(format!("{}", error));
put_log(&err).ok();
err
}
@@ -204,7 +212,7 @@ impl From<std::io::Error> for HError {
impl From<failure::Error> for HError {
fn from(error: failure::Error) -> Self {
// dbg!(&error);
- let err = HError::Error { error: error, backtrace: Backtrace::new() };
+ let err = HError::Error(format!("{}", error));
put_log(&err).ok();
err
}
@@ -231,7 +239,7 @@ impl From<std::sync::mpsc::RecvError> for HError {
impl<T> From<std::sync::mpsc::SendError<T>> for HError {
fn from(error: std::sync::mpsc::SendError<T>) -> Self {
dbg!(&error);
- let err = HError::ChannelSendError(Backtrace::new());
+ let err = HError::ChannelSendError;
put_log(&err).ok();
err
}
@@ -240,7 +248,7 @@ impl<T> From<std::sync::mpsc::SendError<T>> for HError {
impl<T> From<std::sync::PoisonError<T>> for HError {
fn from(_: std::sync::PoisonError<T>) -> Self {
// dbg!("Poisoned Mutex");
- let err = HError::MutexError(Backtrace::new());
+ let err = HError::MutexError;
put_log(&err).ok();
err
}
@@ -249,7 +257,7 @@ impl<T> From<std::sync::PoisonError<T>> for HError {
impl<T> From<std::sync::TryLockError<T>> for HError {
fn from(error: std::sync::TryLockError<T>) -> Self {
// dbg!(&error);
- let err = HError::TryLockError(Backtrace::new());
+ let err = HError::TryLockError;
put_log(&err).ok();
err
}
@@ -258,7 +266,7 @@ impl<T> From<std::sync::TryLockError<T>> for HError {
impl From<std::option::NoneError> for HError {
fn from(error: std::option::NoneError) -> Self {
//dbg!(&error);
- let err = HError::NoneError(Backtrace::new());
+ let err = HError::NoneError;
//put_log(&err).ok();
err
}
@@ -267,7 +275,7 @@ impl From<std::option::NoneError> for HError {
impl From<std::path::StripPrefixError> for HError {
fn from(error: std::path::StripPrefixError) -> Self {
// dbg!(&error);
- let err = HError::StripPrefixError{error: error, backtrace: Backtrace::new() };
+ let err = HError::StripPrefixError{error: error };
put_log(&err).ok();
err
}
@@ -276,7 +284,7 @@ impl From<std::path::StripPrefixError> for HError {
impl From<notify::Error> for HError {
fn from(error: notify::Error) -> Self {
// dbg!(&error);
- let err = HError::INotifyError{error: error, backtrace: Backtrace::new() };
+ let err = HError::INotifyError(format!("{}", error));
put_log(&err).ok();
err
}
diff --git a/src/file_browser.rs b/src/file_browser.rs
index 37d44bb..3a55f14 100644
--- a/src/file_browser.rs
+++ b/src/file_browser.rs
@@ -194,6 +194,7 @@ impl FileBrowser {
let mut list = ListView::new(&core_m,
Files::new_from_path(&main_path)?);
list.animate_slide_up().log();
+ list.content.meta_all();
Ok(list)
}));
@@ -216,7 +217,7 @@ impl FileBrowser {
columns.refresh().log();
- let cwd = File::new_from_path(&cwd).unwrap();
+ let cwd = File::new_from_path(&cwd, None).unwrap();
let dir_events = Arc::new(Mutex::new(vec![]));
let (tx_watch, rx_watch) = channel();
@@ -323,6 +324,7 @@ impl FileBrowser {
})?;
let mut list = ListView::new(&core, files);
+ list.content.meta_all();
if let Some(file) = &selected_file {
list.select_file(file);
@@ -395,7 +397,7 @@ impl FileBrowser {
pub fn goto_bookmark(&mut self) -> HResult<()> {
let path = self.get_boomark()?;
- let path = File::new_from_path(&PathBuf::from(path))?;
+ let path = File::new_from_path(&PathBuf::from(path), None)?;
self.main_widget_goto(&path)?;
Ok(())
}
@@ -664,7 +666,7 @@ impl FileBrowser {
match dir {
Ok(dir) => {
self.columns.widgets.clear();
- let cwd = File::new_from_path(&std::path::PathBuf::from(&dir))?;
+ let cwd = File::new_from_path(&std::path::PathBuf::from(&dir), None)?;
self.cwd = cwd;
let dir = std::path::PathBuf::from(&dir);
let left_dir = std::path::PathBuf::from(&dir);
@@ -752,13 +754,14 @@ impl FileBrowser {
pub fn get_footer(&self) -> HResult<String> {
let xsize = self.get_coordinates()?.xsize();
let ypos = self.get_coordinates()?.position().y();
- let file = self.selected_file()?;
+ let pos = self.main_widget()?.get_selection();
+ let file = self.main_widget()?.content.files.get(pos)?;
let permissions = file.pretty_print_permissions().unwrap_or("NOPERMS".into());
let user = file.pretty_user().unwrap_or("NOUSER".into());
let group = file.pretty_group().unwrap_or("NOGROUP".into());
let mtime = file.pretty_mtime().unwrap_or("NOMTIME".into());
- let target = if let Some(target) = file.target {
+ let target = if let Some(target) = &file.target {
"--> ".to_string() + &target.short_string()
} else { "".to_string() };
diff --git a/src/files.rs b/src/files.rs
index 1a038ac..40d5ac0 100644
--- a/src/files.rs
+++ b/src/files.rs
@@ -1,8 +1,10 @@
use std::cmp::{Ord, Ordering};
use std::ops::Index;
+use std::fs::Metadata;
use std::os::unix::fs::MetadataExt;
use std::path::{Path, PathBuf};
use std::sync::{Arc, Mutex};
+use std::sync::mpsc::Sender;
use std::hash::{Hash, Hasher};
use std::os::unix::ffi::{OsStringExt, OsStrExt};
use std::ffi::{OsStr, OsString};
@@ -16,11 +18,13 @@ use users::{get_current_username,
use chrono::TimeZone;
use failure::Error;
use notify::DebouncedEvent;
+use rayon::prelude::*;
+use rayon::{ThreadPool, ThreadPoolBuilder};
-use crate::fail::{HResult, HError};
-use crate::dirty::{DirtyBit, Dirtyable};
-
-
+use crate::fail::{HResult, HError, ErrorLog};
+use crate::dirty::{AsyncDirtyBit, DirtyBit, Dirtyable};
+use crate::preview::Async;
+use crate::widget::Events;
lazy_static! {
@@ -28,6 +32,23 @@ lazy_static! {
static ref TAGS: Mutex<(bool, Vec<PathBuf>)> = Mutex::new((false, vec![]));
}
+fn make_pool(sender: Option<Sender<Events>>) -> ThreadPool {
+ let sender = Arc::new(Mutex::new(sender));
+ ThreadPoolBuilder::new()
+ .num_threads(8)
+ .exit_handler(move |thread_num| {
+ if thread_num == 0 {
+ if let Ok(lock) = sender.lock() {
+ if let Some(sender) = lock.as_ref() {
+ sender.send(Events::WidgetReady).ok();
+ }
+ }
+ }
+ })
+ .build()
+ .expect("Failed to create thread pool")
+}
+
pub fn load_tags() -> HResult<()> {
std::thread::spawn(|| -> HResult<()> {
let tag_path = crate::paths::tagfile_path()?;
@@ -53,16 +74,19 @@ pub fn tags_loaded() -> HResult<()> {
else { HError::tags_not_loaded() }
}
+
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
pub struct Files {
pub directory: File,
pub files: Vec<File>,
+ pub meta_upto: Option<usize>,
pub sort: SortBy,
pub dirs_first: bool,
pub reverse: bool,
pub show_hidden: bool,
pub filter: Option<String>,
- pub dirty: DirtyBit
+ pub dirty: DirtyBit,
+ pub dirty_meta: AsyncDirtyBit,
}
impl Index<usize> for Files {
@@ -74,12 +98,16 @@ impl Index<usize> for Files {
impl Dirtyable for Files {
- fn get_bit(&self) -> &DirtyBit {
- &self.dirty
+ fn is_dirty(&self) -> bool {
+ self.dirty.is_dirty()
+ }
+
+ fn set_dirty(&mut self) {
+ self.dirty.set_dirty();
}
- fn get_bit_mut(&mut self) -> &mut DirtyBit {
- &mut self.dirty
+ fn set_clean(&mut self) {
+ self.dirty.set_clean();
}
}
@@ -87,6 +115,8 @@ impl Dirtyable for Files {
impl Files {
pub fn new_from_path(path: &Path) -> Result<Files, Error> {
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
+ let dirty = DirtyBit::new();
+ let dirty_meta = AsyncDirtyBit::new();
let files: Vec<_> = direntries?
.iter()
@@ -94,23 +124,29 @@ impl Files {
let name = file.file_name();
let name = name.to_string_lossy();
let path = file.path();
- File::new(&name, path)
+ File::new(&name,
+ path,
+ Some(dirty_meta.clone()))
})
.collect();
let mut files = Files {
- directory: File::new_from_path(&path)?,
+ directory: File::new_from_path(&path, None)?,
files: files,
+ meta_upto: None,
sort: SortBy::Name,
dirs_first: true,
reverse: false,
show_hidden: true,
filter: None,
- dirty: DirtyBit::new()
+ dirty: dirty,
+ dirty_meta: dirty_meta,
};
files.sort();
+
+
if files.files.len() == 0 {
files.files = vec![File::new_placeholder(&path)?];
}
@@ -118,8 +154,12 @@ impl Files {
Ok(files)
}
- pub fn new_from_path_cancellable(path: &Path, stale: Arc<Mutex<bool>>) -> Result<Files, Error> {
+ pub fn new_from_path_cancellable(path: &Path,
+ stale: Arc<Mutex<bool>>)
+ -> Result<Files, Error> {
let direntries: Result<Vec<_>, _> = std::fs::read_dir(&path)?.collect();
+ let dirty = DirtyBit::new();
+ let dirty_meta = AsyncDirtyBit::new();
let files: Vec<_> = direntries?
.iter()
@@ -130,7 +170,9 @@ impl Files {
let name = file.file_name();
let name = name.to_string_lossy();
let path = file.path();
- Some(File::new(&name, path))
+ Some(File::new(&name,
+ path,
+ Some(dirty_meta.clone())))
}
})
.fuse()
@@ -144,14 +186,16 @@ impl Files {
}
let mut files = Files {
- directory: File::new_from_path(&path)?,
+ directory: File::new_from_path(&path, None)?,
files: files,
+ meta_upto: None,
sort: SortBy::Name,
dirs_first: true,
reverse: false,
show_hidden: true,
filter: None,
- dirty: DirtyBit::new()
+ dirty: dirty,
+ dirty_meta: dirty_meta,
};
files.sort();
@@ -163,13 +207,33 @@ impl Files {
Ok(files)
}
+ pub fn get_files(&self) -> Vec<&File> {
+ self.files
+ .iter()
+ .filter(|f| !(self.filter.is_some() &&
+ !f.name.contains(self.filter.as_ref().unwrap())))
+ .filter(|f| !(!self.show_hidden && f.name.starts_with(".")))
+ .collect()
+ }
+
+ pub fn get_files_mut(&mut self) -> Vec<&mut File> {
+ let filter = self.filter.clone();
+ let show_hidden = self.show_hidden;
+ self.files
+ .iter_mut()
+ .filter(|f| !(filter.is_some() &&
+ !f.name.contains(filter.as_ref().unwrap())))
+ .filter(|f| !(!show_hidden && f.name.starts_with(".")))
+ .collect()
+ }
+
pub fn sort(&mut self) {
match self.sort {
SortBy::Name => self
.files
.sort_by(|a, b| alphanumeric_sort::compare_str(&a.name, &b.name)),
SortBy::Size => {
- self.meta_all();
+ self.meta_all_sync();
self.files.sort_by(|a, b| {
if a.meta().unwrap().size() == b.meta().unwrap().size() {
return alphanumeric_sort::compare_str(&b.name, &a.name);
@@ -178,7 +242,7 @@ impl Files {
});
}
SortBy::MTime => {
- self.meta_all();
+ self.meta_all_sync();
self.files.sort_by(|a, b| {
if a.meta().unwrap().mtime() == b.meta().unwrap().mtime() {
return alphanumeric_sort::compare_str(&a.name, &b.name);
@@ -227,25 +291,14 @@ impl Files {
self.show_hidden = !self.show_hidden
}
- pub fn reload_files(&mut self) {
- let dir = self.directory.clone();
- let files = Files::new_from_path(&dir.path()).unwrap();
- let files = files
- .files
- .into_iter()
- .skip_while(|f| f.name.starts_with(".") && !self.show_hidden )
- .collect();
-
- self.files = files;
- self.set_dirty();
- }
-
pub fn handle_event(&mut self, event: &DebouncedEvent) -> HResult<()> {
match event {
DebouncedEvent::Create(path) => {
self.path_in_here(&path)?;
- let file = File::new_from_path(&path)?;
+ let file = File::new_from_path(&path,
+ Some(self.dirty_meta.clone()))?;
self.files.push(file);
+ self.sort();
},
DebouncedEvent::Write(path) | DebouncedEvent::Chmod(path) => {
self.path_in_here(&path)?;
@@ -262,6 +315,7 @@ impl Files {
let mut file = self.find_file_with_path(&old_path)?;
file.name = new_path.file_name()?.to_string_lossy().to_string();
file.path = new_path.into();
+ file.reload_meta()?;
},
DebouncedEvent::Error(err, path) => {
dbg!(err);
@@ -287,17 +341,58 @@ impl Files {
self.files.iter_mut().find(|file| file.path == path)
}
+ pub fn meta_all_sync(&mut self) -> HResult<()> {
+ for file in self.files.iter_mut() {
+ if !file.meta_processed {
+ let path = file.path.clone();
+ file.meta = Async::new(Box::new(move|_| {
+ let meta = std::fs::metadata(&path)?;
+ Ok(meta)
+ }));
+ file.meta.wait()?;
+ }
+ }
+ self.dirty_meta.set_dirty();
+ Ok(())
+ }
+
pub fn meta_all(&mut self) {
- let len = self.files.len();
- self.meta_upto(len);
+ let len = self.len();
+ self.meta_upto(len, None);
}
- pub fn meta_upto(&mut self, to: usize) {
- for file in self.files.iter_mut().take(to) {
- file.get_meta().ok();
+ pub fn meta_upto(&mut self, to: usize, sender: Option<Sender<Events>>) {
+ let meta_files = if self.meta_upto > Some(to) {
+ self.meta_upto.unwrap()
+ } else {
+ if to > self.len() {
+ self.len()
+ } else {
+ to
+ }
+ };
+
+ if self.meta_upto >= Some(meta_files) && !self.dirty_meta.is_dirty() { return }
+
+ self.set_dirty();
+ self.dirty_meta.set_clean();
+
+ let meta_pool = make_pool(sender.clone());
+ let dirsize_pool = make_pool(sender);
+
+ for file in self.files.iter_mut().take(meta_files) {
+ if !file.meta_processed {
+ file.take_meta(&meta_pool).ok();
+ }
+ if file.is_dir() {
+ file.take_dirsize(&dirsize_pool).ok();
+ }
}
+
+ self.meta_upto = Some(meta_files);
}
+
pub fn set_filter(&mut self, filter: Option<String>) {
self.filter = filter;
self.set_dirty();
@@ -308,15 +403,7 @@ impl Files {
}
pub fn len(&self) -> usize {
- match &self.filter {
- None => self.files.len(),
- Some(filter) => {
- self.files
- .iter()
- .filter(|f| f.name.contains(filter))
- .count()
- }
- }
+ self.get_files().len()
}
pub fn get_selected(&self) -> Vec<&File> {
@@ -370,14 +457,51 @@ impl Hash for File {
impl Eq for File {}
-#[derive(Debug, Clone)]
+impl Clone for File {
+ fn clone(&self) -> Self {
+ let meta = self.meta.value.clone();
+ let meta = match meta {
+ Ok(meta) => Async::new_with_value(meta.clone()),
+ Err(_) => File::make_async_meta(&self.path, self.dirty_meta.clone())
+ };
+
+ let dirsize = if let Some(ref dirsize) = self.dirsize {
+ let dirsize = dirsize.value.clone();
+ let dirsize = match dirsize {
+ Ok(dirsize) => Async::new_with_value(dirsize),
+ Err(_) => File::make_async_dirsize(&self.path,
+ self.dirty_meta.clone())
+ };
+ Some(dirsize)
+ } else { None };
+
+ File {
+ name: self.name.clone(),
+ path: self.path.clone(),
+ kind: self.kind.clone(),
+ dirsize: dirsize,
+ target: self.target.clone(),
+ color: self.color.clone(),
+ meta: meta,
+ dirty_meta: self.dirty_meta.clone(),
+ meta_processed: self.meta_processed.clone(),
+ selected: self.selected.clone(),
+ tag: self.tag.clone()
+ }
+ }
+}
+
+#[derive(Debug)]
pub struct File {
pub name: String,
pub path: PathBuf,
pub kind: Kind,
+ pub dirsize: Option<Async<usize>>,
pub target: Option<PathBuf>,
pub color: Option<lscolors::Color>,
- pub meta: Option<std::fs::Metadata>,
+ pub meta: Async<Metadata>,
+ pub dirty_meta: Option<AsyncDirtyBit>,
+ pub meta_processed: bool,
pub selected: bool,
pub tag: Option<bool>
// flags: Option<String>,
@@ -387,63 +511,138 @@ impl File {
pub fn new(
name: &str,
path: PathBuf,
- ) -> File {
+ dirty_meta: Option<AsyncDirtyBit>) -> File {
let tag = check_tag(&path).ok();
+ let meta = File::make_async_meta(&path, dirty_meta.clone());
+ let dirsize = if path.is_dir() {
+ Some(File::make_async_dirsize(&path, dirty_meta.clone()))
+ } else { None };
File {
name: name.to_string(),
kind: if path.is_dir() { Kind::Directory } else { Kind::File },
path: path,
+ dirsize: dirsize,
target: None,
- meta: None,
+ meta: meta,
+ meta_processed: false,
+ dirty_meta: dirty_meta,
color: None,
selected: false,
tag: tag,
}
}
- pub fn new_from_path(path: &Path) -> HResult<File> {
+ pub fn new_from_path(path: &Path,
+ dirty_meta: Option<AsyncDirtyBit>) -> HResult<File> {
let pathbuf = path.to_path_buf();
let name = path
.file_name()
.map(|name| name.to_string_lossy().to_string())
.unwrap_or("/".to_string());
- Ok(File::new(&name, pathbuf))
+ Ok(File::new(&name, pathbuf, dirty_meta))
}
pub fn new_placeholder(path: &Path) -> Result<File, Error> {
- let mut file = File::new_from_path(path)?;
+ let mut file = File::new_from_path(path, None)?;
file.name = "<empty>".to_string();
file.kind = Kind::Placeholder;
Ok(file)
}
- pub fn meta(&self) -> HResult<std::fs::Metadata> {
- match &self.meta {
- Some(meta) => Ok(meta.clone()),
- None => { Ok(std::fs::symlink_metadata(&self.path)?) }
+ pub fn make_async_meta(path: &PathBuf,
+ dirty_meta: Option<AsyncDirtyBit>) -> Async<Metadata> {
+ let path = path.clone();
+
+ let mut meta = Async::new(Box::new(move |stale| {
+ if *stale.lock()? { HError::stale()? }
+ Ok(std::fs::symlink_metadata(&path).unwrap())
+ }));
+ if let Some(dirty_meta) = dirty_meta {
+ meta.on_ready(Box::new(move |_| {
+ let mut dirty_meta = dirty_meta.clone();
+ dirty_meta.set_dirty();
+
+ Ok(())
+ }));
}
+ meta
}
- pub fn get_meta(&mut self) -> HResult<()> {
- if let Some(_) = self.meta { return Ok(()) }
+ pub fn make_async_dirsize(path: &PathBuf,
+ dirty_meta: Option<AsyncDirtyBit>) -> Async<usize> {
+ let path = path.clone();
- let meta = std::fs::symlink_metadata(&self.path)?;
- let color = self.get_color(&meta);
- let target = if meta.file_type().is_symlink() {
- self.path.read_link().ok()
- } else { None };
+ let mut dirsize = Async::new(Box::new(move |stale| {
+ if *stale.lock()? { HError::stale()? }
+ Ok(std::fs::read_dir(&path)?.count())
+ }));
+ if let Some(dirty_meta) = dirty_meta {
+ dirsize.on_ready(Box::new(move |_| {
+ let mut dirty_meta = dirty_meta.clone();
+ dirty_meta.set_dirty();
+
+ Ok(())
+ }));
+ }
+ dirsize
+ }
+
+ pub fn meta(&self) -> HResult<&Metadata> {
+ self.meta.get()
+ }
+
+ fn take_dirsize(&mut self, pool: &ThreadPool) -> HResult<()> {
+ let dirsize = self.dirsize.as_mut()?;
+ if let Ok(_) = dirsize.value { return Ok(()) }
+
+ match dirsize.take_async() {
+ Ok(_) => {},
+ Err(HError::AsyncNotReadyError) => { dirsize.run_pooled(&*pool).ok(); },
+ Err(HError::AsyncAlreadyTakenError) => {},
+ Err(HError::NoneError) => {},
+ err @ Err(_) => { err?; }
+ }
+ Ok(())
+ }
+
+ pub fn take_meta(&mut self, pool: &ThreadPool) -> HResult<()> {
+ if self.meta_processed { return Ok(()) }
+
+ match self.meta.take_async() {
+ Ok(_) => {},
+ Err(HError::AsyncNotReadyError) => { self.meta.run_pooled(&*pool).ok(); },
+ Err(HError::AsyncAlreadyTakenError) => {},
+ Err(HError::NoneError) => {},
+ err @ Err(_) => { err?; }
+ }
+
+ if let Ok(meta) = self.meta.get() {
+ let color = self.get_color(&meta);
+ let target = if meta.file_type().is_symlink() {
+ self.path.read_link().ok()
+ } else { None };
+
+ self.color = color;
+ self.target = target;
+ self.meta_processed = true;
+
+ return Ok(())
+ }
- self.meta = Some(meta);
- self.color = color;
- self.target = target;
Ok(())
}
pub fn reload_meta(&mut self) -> HResult<()> {
- self.meta = None;
- self.get_meta()
+ self.dirty_meta.as_mut()?.set_dirty();
+ self.meta_processed = false;
+ self.meta = File::make_async_meta(&self.path, self.dirty_meta.clone());
+ if self.dirsize.is_some() {
+ self.dirsize
+ = Some(File::make_async_dirsize(&self.path, self.dirty_meta.clone()));
+ }
+ Ok(())
}
fn get_color(&self, meta: &std::fs::Metadata) -> Option<lscolors::Color> {
@@ -454,13 +653,8 @@ impl File {
}
pub fn calculate_size(&self) -> HResult<(u64, String)> {
- if self.is_dir() {
- let dir_iterator = std::fs::read_dir(&self.path);
- match dir_iterator {
- Ok(dir_iterator) => return Ok((dir_iterator.count() as u64,
- "".to_string())),
- Err(_) => return Ok((0, "".to_string()))
- }
+ if let Some(ref dirsize) = self.dirsize {
+ return Ok((dirsize.value.clone()? as u64, "".to_string()))
}
@@ -496,7 +690,7 @@ impl File {
pub fn parent_as_file(&self) -> HResult<File> {
let pathbuf = self.parent()?;
- File::new_from_path(&pathbuf)
+ File::new_from_path(&pathbuf, None)
}
pub fn grand_parent(&self) -> Option<PathBuf> {
@@ -505,7 +699,7 @@ impl File {
pub fn grand_parent_as_file(&self) -> HResult<File> {
let pathbuf = self.grand_parent()?;
- File::new_from_path(&pathbuf)
+ File::new_from_path(&pathbuf, None)
}