//! Works ordering the file system objects use super::*; use std::{ collections::BTreeSet, path::{ PathBuf, Path, }, fs::Metadata, sync::Arc, }; #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Default)] pub enum OrderBy { #[default] BirthTime, ChangeTime, AccessTime, ModifiedTime, } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct Config { pub by: OrderBy, } /// Information from a file #[derive(Debug, Clone)] pub struct FileInfo { state: Arc, //TODO: XXX: This will be a *severe* slowdown... How can we have just a reference here? Thread local? `tokio::task_local!`? Global static? path: PathBuf, meta: Metadata, } impl FileInfo { /// Returns a factory function that produces `FileInfo`s with this config. #[inline] pub fn factory<'a, T: Into>>(config: T) -> impl Fn(PathBuf, Metadata) -> FileInfo + 'a where T: 'a { let v = config.into(); move |path, meta| -> FileInfo { FileInfo::new(Arc::clone(&v), path, meta) } } #[inline] pub fn new(config: impl Into>, path: PathBuf, meta: Metadata) -> Self { Self { state: config.into(), path, meta } } #[inline] pub fn path(&self) -> &Path { &self.path } #[inline] pub fn metadata(&self) -> &Metadata { &self.meta } #[inline] pub fn config(&self) -> &Config { &self.state } #[inline] pub fn config_cell(&self) -> &Arc { &self.state } } impl FileInfo { #[inline] fn get_ordered_unix_time(&self) -> i64 { use std::os::unix::prelude::*; //use std::os::linux::fs::MetadataExt as _ ; // Doesn't show birth in `statx()` :/ use std::time::SystemTime; match self.state.by { OrderBy::AccessTime => self.meta.atime(), OrderBy::ChangeTime => self.meta.ctime(), OrderBy::ModifiedTime => self.meta.mtime(), // XXX: Eh, why can't we just get `st_stat` from this? These `expect()`s are slow... OrderBy::BirthTime => self.meta.created() .expect("Failed to get creation time").duration_since(SystemTime::UNIX_EPOCH) .expect("Invalid creation timestamp").as_secs() as i64,//.as_secs_f64().floor() as u64 } //XXX: Should we support nsec precision? } } impl order::Orderer for FileInfo { #[inline(always)] fn order(a: &FileInfo, b: &FileInfo) -> std::cmp::Ordering { match Ord::cmp(&a.get_ordered_unix_time(), &b.get_ordered_unix_time()) { std::cmp::Ordering::Equal => { // If times are equal, compare by path to avoid all files with same timecode overwriting eachother. Ord::cmp(a.path(), b.path()) //TODO: How to handle the `-R` case nested ordering... Uhh... Recursive interface for `FSTimeMap`, like `NestedFSTimeMap` or something? }, non_eq => non_eq, } } } /// Maps files based on their time #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Default)] pub struct FSTimeMap(Arc, BTreeSet>); impl FSTimeMap { #[inline] pub fn new(config: impl Into) -> Self { Self(Arc::new(config.into()), BTreeSet::new()) } #[inline] pub fn iter(&self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator + std::iter::FusedIterator + '_ { self.1.iter().map(|x| x.inner()) } #[inline] pub fn into_iter(self) -> impl Iterator + ExactSizeIterator + DoubleEndedIterator + std::iter::FusedIterator + 'static { self.1.into_iter().map(|x| x.into_inner()) } #[inline] pub fn insert(&mut self, file: FileInfo) { self.1.insert(order::Ordered::new(file)); } #[inline] pub fn len(&self) -> usize { self.1.len() } }