You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
157 lines
3.7 KiB
157 lines
3.7 KiB
//! 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<Config>, //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<Arc<Config>>>(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<Arc<Config>>, 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<Config>
|
|
{
|
|
&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<FileInfo> 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<Config>, BTreeSet<order::Ordered<FileInfo, FileInfo>>);
|
|
|
|
impl FSTimeMap
|
|
{
|
|
#[inline]
|
|
pub fn new(config: impl Into<Config>) -> Self
|
|
{
|
|
Self(Arc::new(config.into()), BTreeSet::new())
|
|
}
|
|
|
|
#[inline]
|
|
pub fn iter(&self) -> impl Iterator<Item = &'_ FileInfo> + ExactSizeIterator + DoubleEndedIterator + std::iter::FusedIterator + '_
|
|
{
|
|
self.1.iter().map(|x| x.inner())
|
|
}
|
|
|
|
#[inline]
|
|
pub fn into_iter(self) -> impl Iterator<Item = FileInfo> + 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()
|
|
}
|
|
}
|