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.
enumerate-ordered/src/work.rs

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()
}
}