use std::fmt;
use std::path::{Path, PathBuf};
use super::metadata::{FileMetadata, FileType};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ListSort {
Size,
Mtime,
Depth,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SortDirection {
Asc,
Desc,
}
#[derive(Debug, Clone)]
pub struct ListOptions {
pub(super) direction: Option<SortDirection>,
pub(super) sort: Option<ListSort>,
pub(super) ancestor: Option<PathBuf>,
pub(super) parent: Option<PathBuf>,
pub(super) file_type: Option<FileType>,
pub(super) is_invalid: bool,
}
impl Default for ListOptions {
#[cfg_attr(coverage_nightly, coverage(off))]
fn default() -> Self {
Self::new()
}
}
impl ListOptions {
pub fn new() -> Self {
Self {
direction: None,
sort: None,
ancestor: None,
parent: None,
file_type: None,
is_invalid: false,
}
}
pub fn descendants_of<P: AsRef<Path>>(mut self, directory: P) -> Self {
if self.parent.is_some() {
self.is_invalid = true;
return self;
}
self.ancestor = Some(directory.as_ref().to_path_buf());
self
}
pub fn children_of<P: AsRef<Path>>(mut self, directory: P) -> Self {
if self.ancestor.is_some() {
self.is_invalid = true;
return self;
}
self.parent = Some(directory.as_ref().to_path_buf());
self
}
pub fn file_type(mut self, file_type: FileType) -> Self {
if self.sort == Some(ListSort::Size) {
self.is_invalid = true;
return self;
}
self.file_type = Some(file_type);
self
}
pub fn by_depth(mut self) -> Self {
if self.sort.is_some() {
self.is_invalid = true;
return self;
}
self.sort = Some(ListSort::Depth);
self
}
pub fn by_mtime(mut self) -> Self {
if self.sort.is_some() {
self.is_invalid = true;
return self;
}
self.sort = Some(ListSort::Mtime);
self
}
pub fn by_size(mut self) -> Self {
if self.sort.is_some() || self.file_type.is_some() {
self.is_invalid = true;
return self;
}
self.sort = Some(ListSort::Size);
self
}
pub fn asc(mut self) -> Self {
if self.direction.is_some() {
self.is_invalid = true;
return self;
}
self.direction = Some(SortDirection::Asc);
self
}
pub fn desc(mut self) -> Self {
if self.direction.is_some() {
self.is_invalid = true;
return self;
}
self.direction = Some(SortDirection::Desc);
self
}
}
#[derive(Debug)]
pub struct ListEntry {
pub(super) path: PathBuf,
pub(super) metadata: FileMetadata,
}
impl ListEntry {
pub fn path(&self) -> &Path {
&self.path
}
pub fn into_path(self) -> PathBuf {
self.path
}
pub fn metadata(&self) -> &FileMetadata {
&self.metadata
}
}
pub type ListMapFunc = Box<dyn FnMut(&rusqlite::Row<'_>) -> rusqlite::Result<ListEntry>>;
#[ouroboros::self_referencing]
struct ListEntriesInner<'conn> {
stmt: rusqlite::Statement<'conn>,
#[borrows(mut stmt)]
#[covariant]
iter: rusqlite::MappedRows<'this, ListMapFunc>,
}
impl<'conn> fmt::Debug for ListEntriesInner<'conn> {
#[cfg_attr(coverage_nightly, coverage(off))]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ListEntries").finish_non_exhaustive()
}
}
fn build_list_entries_inner(
stmt: rusqlite::Statement,
params: Vec<Box<dyn rusqlite::ToSql>>,
map_func: ListMapFunc,
) -> crate::Result<ListEntriesInner> {
ListEntriesInnerTryBuilder {
stmt,
iter_builder: |stmt| {
stmt.query_map(
params
.iter()
.map(AsRef::as_ref)
.collect::<Vec<_>>()
.as_slice(),
map_func,
)
.map_err(crate::Error::from)
},
}
.try_build()
}
#[derive(Debug)]
pub struct ListEntries<'conn> {
inner: ListEntriesInner<'conn>,
}
impl<'conn> ListEntries<'conn> {
pub(super) fn new(
stmt: rusqlite::Statement<'conn>,
params: Vec<Box<dyn rusqlite::ToSql>>,
map_func: ListMapFunc,
) -> crate::Result<Self> {
Ok(Self {
inner: build_list_entries_inner(stmt, params, map_func)?,
})
}
}
impl<'conn> Iterator for ListEntries<'conn> {
type Item = crate::Result<ListEntry>;
fn next(&mut self) -> Option<Self::Item> {
self.inner
.with_iter_mut(|iter| iter.next())
.map(|item| item.map_err(crate::Error::from))
}
}