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