@ -1,6 +1,79 @@
use super ::* ;
use std ::collections ::HashMap ;
use std ::path ::PathBuf ;
use std ::path ::{ Path , PathBuf } ;
use std ::borrow ::Borrow ;
use std ::iter ::FusedIterator ;
use std ::cell ::RefCell ;
#[ derive(Debug, Clone, PartialEq, Eq) ]
/// A reference to an INode within a graph.
pub struct INodeRef < ' a > ( & ' a INodeInfoGraph , INode ) ;
impl < ' a > AsRef < Path > for INodeRef < ' a >
{
fn as_ref ( & self ) -> & Path
{
self . 0. paths_reverse . get ( & self . 1 ) . unwrap ( )
}
}
impl < ' a > Borrow < INode > for INodeRef < ' a >
{
fn borrow ( & self ) -> & INode
{
& self . 1
}
}
impl < ' a > INodeRef < ' a >
{
/// The owning graph of this INode reference.
#[ inline ] pub fn graph ( & self ) -> & INodeInfoGraph
{
self . 0
}
/// The `FsInfo` for this INode
#[ inline ] pub fn info ( & self ) -> & FsInfo
{
self . 0. inodes . get ( & self . 1 ) . unwrap ( )
}
/// Compute the total size of this INode.
///
/// If this is a file, it is O(1). Otherwise, recursively compute the sizes of all children.
#[ inline ] pub fn size ( & self ) -> u64
{
self . 0. lookup_size_or ( & self . 1 , move | | {
match self . info ( )
{
FsInfo ::File ( sz , _ ) = > * sz ,
FsInfo ::Directory ( _ ) = > {
self . children ( ) . map ( | x | x . size ( ) ) . sum ( )
} ,
}
} )
}
/// The internal INode
#[ inline ] pub fn inode ( & self ) -> INode
{
self . 1
}
/// The path this INode refers to
#[ inline ] pub fn path ( & self ) -> & Path
{
self . as_ref ( )
}
/// Get an iterator over the children of this INode, if it has children.
#[ inline ] pub fn children ( & self ) -> Children < ' a >
{
self . 0. children_of ( & self . 1 )
}
}
/// Contains a graph of all paths and inodes that were successfully stat'd
#[ derive(Debug, Clone, PartialEq, Eq) ]
@ -8,19 +81,130 @@ pub struct INodeInfoGraph
{
inodes : HashMap < INode , FsInfo > , // FsInfo contains parent INode that can be used to look up again in this table
paths : HashMap < PathBuf , INode > , // map absolute paths to INodes to be looked up in `inodes` table.
paths_reverse : HashMap < INode , PathBuf > , // reverse lookup of INode to PathBuf,
children : HashMap < INode , Vec < INode > > , //reverse lookup for directory INodes and their parent INodes
total_sizes_cached : RefCell < HashMap < INode , u64 > > , // the total sizes of all INodes
}
impl INodeInfoGraph
{
fn lookup_insert_size < F > ( & self , node : INode , with : F ) -> u64
where F : FnOnce ( ) -> u64
{
let w = with ( ) ;
if let Ok ( mut cache ) = self . total_sizes_cached . try_borrow_mut ( )
{
cache . insert ( node , w ) ;
w
} else {
w
}
}
fn lookup_size_or < F > ( & self , node : impl Borrow < INode > , or : F ) -> u64
where F : FnOnce ( ) -> u64
{
if let Ok ( cache ) = self . total_sizes_cached . try_borrow ( ) {
let node = node . borrow ( ) ;
if let Some ( sz ) = cache . get ( node ) {
return * sz ;
}
}
self . lookup_insert_size ( * node . borrow ( ) , or )
}
/// Create a new graph from these linked `HashMap`s
#[ inline ] pub fn new ( inodes : HashMap < INode , FsInfo > , paths : HashMap < PathBuf , INode > ) -> Self
{
Self {
children : HashMap ::with_capacity ( inodes . len ( ) ) ,
paths_reverse : HashMap ::with_capacity ( paths . len ( ) ) ,
total_sizes_cached : RefCell ::new ( HashMap ::with_capacity ( inodes . len ( ) ) ) ,
inodes ,
paths
paths ,
}
. compute_child_table ( )
. compute_reverse_path_table ( )
}
#[ inline ] fn compute_child_table ( mut self ) -> Self
{
for ( node , info ) in self . inodes . iter ( )
{
match info {
FsInfo ::Directory ( parent_node ) |
FsInfo ::File ( _ , parent_node ) = > {
self . children . entry ( * parent_node ) . or_insert_with ( | | Vec ::new ( ) ) . push ( * node ) ;
} ,
}
}
self
}
#[ inline ] fn compute_reverse_path_table ( mut self ) -> Self
{
self . paths_reverse . extend ( self . paths . iter ( ) . map ( | ( x , y ) | ( * y , x . clone ( ) ) ) ) ;
self
}
/// Get the FsInfo of this `INode`
#[ inline ] pub fn get_info ( & self , node : impl Borrow < INode > ) -> Option < & FsInfo >
{
self . inodes . get ( node . borrow ( ) )
}
/// An iterator over top-level children of this node
pub fn children_of ( & self , node : impl Borrow < INode > ) -> Children < ' _ >
{
Children ( self , match self . children . get ( node . borrow ( ) ) {
Some ( slc ) = > slc . iter ( ) ,
_ = > [ ] . iter ( ) ,
} )
}
/// An iterator over all the directories in this
pub fn directories ( & self ) -> Directories < ' _ >
{
Directories ( self , self . children . keys ( ) )
}
}
/// An iterator over all directories in a graph
#[ derive(Debug, Clone) ]
pub struct Directories < ' a > ( & ' a INodeInfoGraph , std ::collections ::hash_map ::Keys < ' a , INode , Vec < INode > > ) ;
impl < ' a > Iterator for Directories < ' a >
{
type Item = INodeRef < ' a > ;
#[ inline ] fn next ( & mut self ) -> Option < Self ::Item >
{
self . 1. next ( ) . map ( | x | INodeRef ( self . 0 , * x ) )
}
#[ inline ] fn size_hint ( & self ) -> ( usize , Option < usize > ) {
self . 1. size_hint ( )
}
}
impl < ' a > ExactSizeIterator for Directories < ' a > { }
impl < ' a > FusedIterator for Directories < ' a > { }
/// The immediate children of a specific INode
#[ derive(Debug, Clone) ]
pub struct Children < ' a > ( & ' a INodeInfoGraph , std ::slice ::Iter < ' a , INode > ) ;
impl < ' a > Iterator for Children < ' a >
{
type Item = INodeRef < ' a > ;
#[ inline ] fn next ( & mut self ) -> Option < Self ::Item >
{
self . 1. next ( ) . map ( | x | INodeRef ( self . 0 , * x ) )
}
fn size_hint ( & self ) -> ( usize , Option < usize > ) {
self . 1. size_hint ( )
}
}
impl < ' a > ExactSizeIterator for Children < ' a > { }
impl < ' a > FusedIterator for Children < ' a > { }
impl < ' a > DoubleEndedIterator for Children < ' a >
{
#[ inline ] fn next_back ( & mut self ) -> Option < Self ::Item > {
self . 1. next_back ( ) . map ( | x | INodeRef ( self . 0 , * x ) )
}
//TODO: Get whole directory structure. Find largest, etc.
//TODO: Order by largest file size, get, iter, etc
//TODO: Group children to parent (child FSInfos all have references to their parent INode, but parents don't have references to their children. Top level FsInfos will also have parent INodes that don't appear in the map as keys.)
}