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.
410 lines
9.4 KiB
410 lines
9.4 KiB
use super::*;
|
|
use std::collections::HashMap;
|
|
use std::path::{Path,PathBuf};
|
|
use std::borrow::Borrow;
|
|
|
|
/// Contains a graph of all paths and inodes that were successfully stat'd
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
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
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct INodeInfoGraphEntry<'a>
|
|
{
|
|
master: &'a INodeInfoGraph,
|
|
inode: INode,
|
|
}
|
|
|
|
impl<'a> INodeInfoGraphEntry<'a>
|
|
{
|
|
/// Create an iterator over children of this graph item.
|
|
///
|
|
/// # Note
|
|
/// Returns `None` if this is not a directory item.
|
|
pub fn level(&self) -> Option<Level<'a>>
|
|
{
|
|
match self.master.inodes.get(&self.inode)
|
|
{
|
|
Some(FsInfo::Directory(parent)) => {
|
|
Some(
|
|
Level{
|
|
master: self.master,
|
|
inode: self.inode.clone(),
|
|
children: match self.master.children.get(&self.inode) {
|
|
Some(iter) => iter.iter(),
|
|
_ => [].iter(),
|
|
},
|
|
}
|
|
)
|
|
},
|
|
Some(FsInfo::File(_, _)) => None,
|
|
None => {
|
|
panic!("No lookup information for inode {:?}", self.inode)
|
|
}//Some(Level{master: self.master, inode: None, children: [].iter()}),
|
|
}
|
|
}
|
|
|
|
/// The `FsInfo` for this item.
|
|
pub fn info(&self) -> &FsInfo
|
|
{
|
|
self.master.inodes.get(&self.inode).unwrap()
|
|
}
|
|
/// The path for this inode
|
|
pub fn path(&self) -> &PathBuf
|
|
{
|
|
self.master.paths_reverse.get(&self.inode).unwrap()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct Level<'a>
|
|
{
|
|
master: &'a INodeInfoGraph,
|
|
inode: INode,// Should only ever be `Directory` for `Level`.
|
|
children: std::slice::Iter<'a, INode>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct TopLevel<'a>
|
|
{
|
|
master: &'a INodeInfoGraph,
|
|
children: std::vec::IntoIter<&'a INode>,
|
|
}
|
|
|
|
impl<'a> Iterator for TopLevel<'a>
|
|
{
|
|
type Item = INodeInfoGraphEntry<'a>;
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
let master = self.master;
|
|
self.children.next().map(|inode| {
|
|
INodeInfoGraphEntry{
|
|
master,
|
|
inode: inode.clone(),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
impl<'a> Iterator for Level<'a>
|
|
{
|
|
type Item = INodeInfoGraphEntry<'a>;
|
|
fn next(&mut self) -> Option<Self::Item>
|
|
{
|
|
let master = self.master;
|
|
self.children.next().map(|inode| {
|
|
INodeInfoGraphEntry{
|
|
master,
|
|
inode: inode.clone(),
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
impl INodeInfoGraph
|
|
{
|
|
/// Total size of all files
|
|
pub fn total_size(&self) -> u64
|
|
{
|
|
self.inodes.iter().map(|(_, v)| {
|
|
match v {
|
|
FsInfo::File(len, _) => *len,
|
|
_ => 0,
|
|
}
|
|
}).sum()
|
|
}
|
|
/// An iterator over the top level of items
|
|
pub fn top_level(&self) -> TopLevel<'_>
|
|
{
|
|
let top_level = self.inodes.iter().filter(|(node, _)| {
|
|
!self.inodes.contains_key(node)
|
|
});
|
|
TopLevel {
|
|
master: self,
|
|
children: top_level.map(|(k, _)| k).collect::<Vec<_>>().into_iter(),
|
|
}
|
|
}
|
|
/// 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()),
|
|
inodes,
|
|
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
|
|
}
|
|
/// Total number of items in the graph
|
|
#[inline] pub fn len(&self) -> usize
|
|
{
|
|
self.children.len()
|
|
}
|
|
/// 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<'_>
|
|
{
|
|
todo!();
|
|
/*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<'_>
|
|
{
|
|
todo!()//Directories(self, self.children.keys())
|
|
}*/
|
|
|
|
/// Convert into a hierarchical representation
|
|
pub fn into_hierarchical(self) -> HierarchicalINodeGraph
|
|
{
|
|
HierarchicalINodeGraph::create(self)
|
|
}
|
|
|
|
/// Convert into a hierarchical representation
|
|
pub fn create_hierarchical(&self) -> HierarchicalINodeGraph
|
|
{
|
|
HierarchicalINodeGraph::create_from(self)
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature="inspect", derive(serde::Serialize, serde::Deserialize))]
|
|
enum NodeKind
|
|
{
|
|
Directory(Vec<PathBuf>, Option<u64>),
|
|
File(u64),
|
|
}
|
|
|
|
impl NodeKind
|
|
{
|
|
pub fn len(&self) -> Option<u64>
|
|
{
|
|
match self {
|
|
Self::File(f) => Some(*f),
|
|
Self::Directory(_, o) => *o,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature="inspect", derive(serde::Serialize, serde::Deserialize))]
|
|
struct HierarchicalNode
|
|
{
|
|
kind: NodeKind,
|
|
inode: INode,
|
|
}
|
|
|
|
impl HierarchicalNode
|
|
{
|
|
#[inline(always)] pub fn len(&self) -> Option<u64>
|
|
{
|
|
self.kind.len()
|
|
}
|
|
|
|
pub fn is_dir(&self) -> bool
|
|
{
|
|
match self.kind {
|
|
NodeKind::Directory(_, _) => true,
|
|
_ => false
|
|
}
|
|
}
|
|
#[inline] pub fn is_file(&self) -> bool
|
|
{
|
|
!self.is_dir()
|
|
}
|
|
}
|
|
|
|
/// A hierarchical graph of node sizes
|
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
#[cfg_attr(feature="inspect", derive(serde::Serialize, serde::Deserialize))]
|
|
pub struct HierarchicalINodeGraph
|
|
{
|
|
table: HashMap<PathBuf, HierarchicalNode>
|
|
}
|
|
|
|
impl HierarchicalINodeGraph
|
|
{
|
|
/// Compute the sizes of directories in the graph
|
|
pub fn compute_recursive_sizes(&mut self)
|
|
{
|
|
fn compute_for_dir(this: &HashMap<PathBuf, HierarchicalNode>, children: &Vec<PathBuf>) -> u64
|
|
{
|
|
let mut total = 0;
|
|
for child in children.iter()
|
|
{
|
|
let chl = if let Some(ch) = this.get(child)
|
|
{
|
|
match ch {
|
|
HierarchicalNode { kind: NodeKind::Directory(_, Some(sz)), .. } => {
|
|
total += sz;
|
|
continue;
|
|
},
|
|
HierarchicalNode { kind: NodeKind::Directory(children, _), .. } => {
|
|
children
|
|
},
|
|
HierarchicalNode { kind: NodeKind::File(sz), .. } => {
|
|
total += sz;
|
|
continue;
|
|
},
|
|
}
|
|
} else {
|
|
continue;
|
|
};
|
|
total += compute_for_dir(this, chl);
|
|
}
|
|
total
|
|
}
|
|
let mut length_proxy = HashMap::with_capacity(self.table.len());
|
|
|
|
for (path, hnode) in self.table.iter()
|
|
{
|
|
let (children, len) = match hnode {
|
|
HierarchicalNode { kind: NodeKind::Directory(children, len), .. } => {
|
|
(children, len)
|
|
},
|
|
_ => continue,
|
|
};
|
|
match len {
|
|
Some(sz) => length_proxy.insert(path.clone(), *sz),
|
|
None => length_proxy.insert(path.clone(), compute_for_dir(&self.table, children)),
|
|
};
|
|
}
|
|
|
|
for (pathref, sz) in length_proxy.into_iter()
|
|
{
|
|
self.table.get_mut(&pathref).map(|hnode| {
|
|
match hnode {
|
|
HierarchicalNode { kind: NodeKind::Directory(_, ss), .. } => {
|
|
*ss = Some(sz);
|
|
},
|
|
_ => (),
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
#[inline(always)] fn create(graph: INodeInfoGraph) -> Self
|
|
{
|
|
//eh...
|
|
Self::create_from(&graph)
|
|
}
|
|
fn create_from(graph: &INodeInfoGraph) -> Self
|
|
{
|
|
let mut new = Self {
|
|
table: HashMap::with_capacity(graph.inodes.len()),
|
|
};
|
|
|
|
macro_rules! unwrap {
|
|
($expr:expr) => {
|
|
match $expr {
|
|
Some(ex) => ex,
|
|
_ => continue,
|
|
}
|
|
};
|
|
}
|
|
|
|
for (path, &inode) in graph.paths.iter()
|
|
{
|
|
let path = path.clone();
|
|
//Lookup the INode in graph inode table
|
|
if let Some(fsinfo) = graph.inodes.get(&inode)
|
|
{
|
|
match fsinfo {
|
|
FsInfo::File(sz, _) => {
|
|
new.table.insert(path, HierarchicalNode {
|
|
kind: NodeKind::File(*sz),
|
|
inode,
|
|
});
|
|
},
|
|
FsInfo::Directory(_) => {
|
|
new.table.insert(path, HierarchicalNode {
|
|
kind: NodeKind::Directory(unwrap!(graph.children.get(&inode)).iter().map(|node| {
|
|
graph.paths_reverse.get(node).unwrap().clone()
|
|
}).collect(), None),
|
|
inode,
|
|
});
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
new
|
|
}
|
|
|
|
/// Get the max size and the path with this size
|
|
pub fn path_max_size_for(&self, kind: FsObjKind) -> Option<(&Path, u64)>
|
|
{
|
|
self.table.iter().filter_map(|(path, hi)| {
|
|
match kind {
|
|
FsObjKind::File if hi.is_file() => hi.len().map(|x| (path.as_path(), x)),
|
|
FsObjKind::Directory if hi.is_dir() => hi.len().map(|x| (path.as_path(), x)),
|
|
_ => None
|
|
}
|
|
}).max_by_key(|x| x.1)
|
|
}
|
|
/// Get the max size and the path with this size, whether it's a `Directory` or a `File`.
|
|
pub fn path_max_size(&self) -> Option<(&Path, u64)>
|
|
{
|
|
self.table.iter().filter_map(|(path, hi)| {
|
|
hi.len().map(|x| (path.as_path(), x))
|
|
}).max_by_key(|x| x.1)
|
|
}
|
|
|
|
/// Complete number of items in the tree
|
|
#[inline] pub fn len(&self) -> usize
|
|
{
|
|
self.table.len()
|
|
}
|
|
}
|
|
|
|
impl From<INodeInfoGraph> for HierarchicalINodeGraph
|
|
{
|
|
fn from(from: INodeInfoGraph) -> Self
|
|
{
|
|
Self::create(from)
|
|
}
|
|
}
|
|
|
|
/// A file or directory
|
|
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy)]
|
|
pub enum FsObjKind
|
|
{
|
|
Directory,
|
|
File,
|
|
}
|