use std::{
collections::{HashMap, hash_map::Entry},
ffi::{OsStr, OsString},
iter::FusedIterator,
path::{Path, PathBuf},
};
use crate::{
FileId, FileKind, FileMetadata, FilesystemId, block::FileId as StoreFileId,
file_metadata::RawMetadata, sql::AncestorPathId,
};
#[derive(Debug, Clone)]
pub struct DirectoryEntry {
path: PathBuf,
name: OsString,
file_id: StoreFileId,
kind: FileKind,
metadata: FileMetadata,
filesystem_id: FilesystemId,
}
impl DirectoryEntry {
pub fn name(&self) -> &OsStr {
&self.name
}
pub fn path(&self) -> &Path {
&self.path
}
pub fn kind(&self) -> &FileKind {
&self.kind
}
pub fn file_id(&self) -> FileId {
FileId::new(self.file_id, self.filesystem_id)
}
pub fn metadata(&self) -> &FileMetadata {
&self.metadata
}
}
#[derive(Debug)]
pub struct Children {
rows: Vec<ChildrenRow>,
parent_path: PathBuf,
filesystem_id: FilesystemId,
}
impl Children {
pub(crate) fn new(
rows: Vec<ChildrenRow>,
parent_path: PathBuf,
filesystem_id: FilesystemId,
) -> Self {
Self {
rows,
parent_path,
filesystem_id,
}
}
}
impl Iterator for Children {
type Item = DirectoryEntry;
fn next(&mut self) -> Option<Self::Item> {
let row = self.rows.pop()?;
Some(DirectoryEntry {
path: self.parent_path.join(&row.name),
name: row.name,
file_id: row.file_id,
kind: row.kind,
metadata: FileMetadata::from_raw(row.metadata),
filesystem_id: self.filesystem_id,
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.rows.len(), Some(self.rows.len()))
}
}
impl ExactSizeIterator for Children {}
impl FusedIterator for Children {}
#[derive(Debug)]
pub struct Descendants {
rows: Vec<DescendantsRow>,
path_cache: HashMap<i64, PathBuf>,
remaining_children: HashMap<i64, usize>,
filesystem_id: FilesystemId,
}
impl Descendants {
pub(crate) fn new(
mut rows: Vec<DescendantsRow>,
ancestor_id: AncestorPathId,
base_path: PathBuf,
filesystem_id: FilesystemId,
) -> Self {
let mut remaining_children: HashMap<i64, usize> = HashMap::new();
for row in &rows {
*remaining_children.entry(row.parent_path_id).or_insert(0) += 1;
}
let mut path_cache = HashMap::new();
if remaining_children.contains_key(&ancestor_id.0) {
path_cache.insert(ancestor_id.0, base_path);
}
rows.reverse();
Self {
rows,
path_cache,
remaining_children,
filesystem_id,
}
}
}
impl Iterator for Descendants {
type Item = DirectoryEntry;
fn next(&mut self) -> Option<Self::Item> {
let row = self.rows.pop()?;
let parent_path = self
.path_cache
.get(&row.parent_path_id)
.expect("Parent paths should be returned before their children.");
let path = parent_path.join(&row.name);
if let Entry::Occupied(mut entry) = self.remaining_children.entry(row.parent_path_id) {
*entry.get_mut() -= 1;
if *entry.get() == 0 {
entry.remove();
self.path_cache.remove(&row.parent_path_id);
}
}
if matches!(row.kind, FileKind::Dir) && self.remaining_children.contains_key(&row.path_id) {
self.path_cache.insert(row.path_id, path.clone());
}
Some(DirectoryEntry {
path,
name: row.name,
file_id: row.file_id,
kind: row.kind,
filesystem_id: self.filesystem_id,
metadata: FileMetadata::from_raw(row.metadata),
})
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.rows.len(), Some(self.rows.len()))
}
}
impl ExactSizeIterator for Descendants {}
impl FusedIterator for Descendants {}
#[derive(Debug)]
pub struct ChildrenRow {
pub ancestor_id: AncestorPathId,
pub name: OsString,
pub file_id: StoreFileId,
pub kind: FileKind,
pub metadata: RawMetadata,
}
#[derive(Debug)]
pub struct DescendantsRow {
pub path_id: i64,
pub name: OsString,
pub parent_path_id: i64,
pub file_id: StoreFileId,
pub kind: FileKind,
pub metadata: RawMetadata,
}
#[derive(Debug)]
pub struct WalkEntry<'fs> {
pub(super) path: &'fs Path,
pub(super) name: &'fs OsStr,
pub(super) file_id: StoreFileId,
pub(super) kind: &'fs FileKind,
pub(super) metadata: &'fs FileMetadata,
pub(super) filesystem_id: FilesystemId,
}
impl<'fs> WalkEntry<'fs> {
pub fn name(&self) -> &OsStr {
self.name
}
pub fn path(&self) -> &Path {
self.path
}
pub fn kind(&self) -> &FileKind {
self.kind
}
pub fn file_id(&self) -> FileId {
FileId::new(self.file_id, self.filesystem_id)
}
pub fn metadata(&self) -> &FileMetadata {
self.metadata
}
}
#[derive(Debug)]
pub enum WalkVisit<'fs> {
File(WalkEntry<'fs>),
EnterDir(WalkEntry<'fs>),
LeaveDir(&'fs Path),
}
#[derive(Debug)]
pub enum WalkPredicate<R> {
Continue,
SkipSiblings,
SkipDescendants,
Stop(R),
}
#[derive(Debug)]
pub struct WalkOptions {
pub(super) max_depth: Option<usize>,
pub(super) follow_symlinks: bool,
}
impl Default for WalkOptions {
fn default() -> Self {
Self::new()
}
}
impl WalkOptions {
pub fn new() -> Self {
Self {
max_depth: None,
follow_symlinks: false,
}
}
pub fn max_depth(mut self, depth: Option<usize>) -> Self {
self.max_depth = depth;
self
}
pub fn follow_symlinks(mut self, follow: bool) -> Self {
self.follow_symlinks = follow;
self
}
}