-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(actors): split the pipeline into several actors that could be ru…
…n in parallel if needed
- Loading branch information
Showing
12 changed files
with
344 additions
and
259 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
pub mod action_applicator; | ||
pub mod conflict_handler; | ||
pub mod filter_applicator; | ||
pub mod location_walker; | ||
|
||
/// A trait representing a service in our pipeline | ||
pub trait PipelineService {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
|
138 changes: 138 additions & 0 deletions
138
crates/organize-rs_core/src/actors/filter_applicator.rs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
use crate::{actors::location_walker::DirEntryData, filters::FilterGroupCollection}; | ||
use itertools::{Either, Itertools}; | ||
|
||
|
||
|
||
use crate::{ | ||
filters::{ | ||
FilterApplicationKind, FilterFilterClosureSliceMut, FilterGroup, FilterGroupOperationKind, | ||
FilterKind, | ||
}, | ||
}; | ||
|
||
#[derive(Debug, Default)] | ||
pub struct FilterApplicator { | ||
filters: FilterGroupCollection, | ||
} | ||
|
||
impl FilterApplicator { | ||
pub fn new(filters: FilterGroupCollection) -> Self { | ||
FilterApplicator { filters } | ||
} | ||
|
||
pub fn get_applicable_items(self, entries: DirEntryData) -> DirEntryData { | ||
// extract ignore filters | ||
let (ignore_filters, other_filters): (Vec<_>, Vec<_>) = | ||
self.filters | ||
.iter() | ||
.partition_map(|filter| match filter.mode() { | ||
FilterApplicationKind::None => Either::Left(filter), | ||
_ => Either::Right(filter), | ||
}); | ||
|
||
// split off any / all filters | ||
let (any_filters, all_filters): (Vec<_>, Vec<_>) = | ||
other_filters | ||
.into_iter() | ||
.partition_map(|filter| match filter.mode() { | ||
FilterApplicationKind::Any => Either::Left(filter), | ||
FilterApplicationKind::All => Either::Right(filter), | ||
_ => unreachable!( | ||
"There should be no items left in `FilterModeGroupKind::None`!" | ||
), | ||
}); | ||
|
||
Self::get_filtered_entries(entries, ignore_filters, any_filters, all_filters) | ||
} | ||
|
||
pub fn apply_filters( | ||
entries: Vec<jwalk::DirEntry<((), ())>>, | ||
filters: FilterFilterClosureSliceMut<((), ())>, | ||
) -> Vec<jwalk::DirEntry<((), ())>> { | ||
entries | ||
.into_iter() | ||
.filter(|entry| { | ||
let mut results = vec![]; | ||
filters | ||
.iter_mut() | ||
.for_each(|filter| results.push(filter(entry))); | ||
results.contains(&true) | ||
}) | ||
.collect_vec() | ||
} | ||
|
||
fn filter_group_applies( | ||
filter_group: &FilterGroup<Vec<FilterKind>>, | ||
entry: &jwalk::DirEntry<((), ())>, | ||
) -> bool { | ||
let (matched, not_matched): (Vec<bool>, Vec<bool>) = filter_group | ||
.filters() | ||
.iter() | ||
.map(|single_filter| single_filter.get_filter()(entry)) | ||
.partition(|f| *f); | ||
|
||
match (filter_group.apply(), filter_group.mode()) { | ||
(FilterGroupOperationKind::Exclude, FilterApplicationKind::All) | ||
| (FilterGroupOperationKind::Include, FilterApplicationKind::All) => { | ||
not_matched.is_empty() | ||
} | ||
(FilterGroupOperationKind::Exclude, FilterApplicationKind::Any) | ||
| (FilterGroupOperationKind::Include, FilterApplicationKind::Any) => { | ||
!matched.is_empty() | ||
} | ||
(FilterGroupOperationKind::Exclude, FilterApplicationKind::None) | ||
| (FilterGroupOperationKind::Include, FilterApplicationKind::None) => { | ||
matched.is_empty() | ||
} | ||
} | ||
} | ||
|
||
fn get_filtered_entries( | ||
entries: DirEntryData, | ||
ignore_filters: Vec<&FilterGroup<Vec<FilterKind>>>, | ||
any_filters: Vec<&FilterGroup<Vec<FilterKind>>>, | ||
all_filters: Vec<&FilterGroup<Vec<FilterKind>>>, | ||
) -> DirEntryData { | ||
let filtered_entries = entries | ||
.into_iter() | ||
.flat_map(|entry| { | ||
if !ignore_filters.is_empty() { | ||
ignore_filters | ||
.iter() | ||
// .inspect(|f| println!("Applying ignore filter: {f}")) | ||
.map(|filter| Self::filter_group_applies(filter, &entry)) | ||
.all(|f| matches!(f, false)) | ||
.then_some(entry) | ||
} else { | ||
Some(entry) | ||
} | ||
}) | ||
.flat_map(|entry| { | ||
if !all_filters.is_empty() { | ||
all_filters | ||
.iter() | ||
// .inspect(|f| println!("Applying filter: {f}")) | ||
.map(|filter| Self::filter_group_applies(filter, &entry)) | ||
.all(|f| matches!(f, true)) | ||
.then_some(entry) | ||
} else { | ||
Some(entry) | ||
} | ||
}) | ||
.flat_map(|entry| { | ||
if !any_filters.is_empty() { | ||
any_filters | ||
.iter() | ||
// .inspect(|f| println!("Applying filter: {f}")) | ||
.map(|filter| Self::filter_group_applies(filter, &entry)) | ||
.any(|f| matches!(f, true)) | ||
.then_some(entry) | ||
} else { | ||
Some(entry) | ||
} | ||
}) | ||
.collect_vec(); | ||
|
||
DirEntryData::from(filtered_entries) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,142 @@ | ||
use itertools::Itertools; | ||
use jwalk::{ClientState, DirEntry}; | ||
use std::{fmt::Display, fs::FileType, path::Path, vec::IntoIter}; | ||
|
||
use crate::{ | ||
error::{OrganizeResult, WalkerErrorKind}, | ||
locations::{LocationCollection, LocationKind, MaxDepth, TargetKind}, | ||
}; | ||
|
||
pub struct IterCarry<'it, C: ClientState> { | ||
pub iter: &'it mut dyn Iterator<Item = jwalk::DirEntry<C>>, | ||
} | ||
|
||
#[derive(Debug, Default)] | ||
pub struct DirEntryData(Vec<jwalk::DirEntry<((), ())>>); | ||
|
||
impl IntoIterator for DirEntryData { | ||
type Item = jwalk::DirEntry<((), ())>; | ||
|
||
type IntoIter = IntoIter<DirEntry<((), ())>>; | ||
|
||
fn into_iter(self) -> Self::IntoIter { | ||
self.0.into_iter() | ||
} | ||
} | ||
|
||
impl DirEntryData { | ||
pub fn print_entries(&self) { | ||
let count = self.0.len(); | ||
self.0.iter().for_each(|f| { | ||
println!("{f:?}"); | ||
}); | ||
println!("Total entry count: {count}"); | ||
} | ||
} | ||
|
||
impl Display for DirEntryData { | ||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { | ||
let last_five = self.0.iter().rev().take(5).collect_vec(); | ||
write!( | ||
f, | ||
" | ||
FilterApplicator | ||
Last 5 entries: | ||
{:?} | ||
", | ||
last_five | ||
) | ||
} | ||
} | ||
|
||
impl From<Vec<jwalk::DirEntry<((), ())>>> for DirEntryData { | ||
fn from(value: Vec<jwalk::DirEntry<((), ())>>) -> Self { | ||
Self(value) | ||
} | ||
} | ||
|
||
// impl std::ops::DerefMut for DirEntryData { | ||
// fn deref_mut(&mut self) -> &mut Self::Target { | ||
// &mut self.0 | ||
// } | ||
// } | ||
|
||
// impl std::ops::Deref for DirEntryData { | ||
// type Target = Vec<jwalk::DirEntry<((), ())>>; | ||
|
||
// fn deref(&self) -> &Self::Target { | ||
// &self.0 | ||
// } | ||
// } | ||
|
||
#[derive(Debug, Default)] | ||
pub struct LocationWalker { | ||
locations: LocationCollection, | ||
} | ||
|
||
impl LocationWalker { | ||
pub const DEFAULT_MAX_DEPTH: usize = 0; | ||
|
||
pub fn new(locations: LocationCollection) -> Self { | ||
Self { | ||
locations, | ||
..Default::default() | ||
} | ||
} | ||
|
||
pub fn collect_dir_entry_data(&mut self) -> DirEntryData { | ||
let entries = self | ||
.locations | ||
.iter() | ||
.unique() | ||
.map(|location| match location { | ||
LocationKind::RecursiveWithMaxDepth { | ||
path, | ||
max_depth, | ||
target, | ||
} => Self::populate_entries(path, *max_depth, *target), | ||
LocationKind::NonRecursive { path, target } => { | ||
Self::populate_entries(path, None, *target) | ||
} | ||
LocationKind::BarePath(path) => { | ||
Self::populate_entries(path, None, TargetKind::default()) | ||
} | ||
}) | ||
.flatten_ok() | ||
.flat_map(std::result::Result::ok) | ||
.collect_vec(); | ||
|
||
DirEntryData::from(entries) | ||
} | ||
|
||
fn populate_entries<A>( | ||
path: A, | ||
max_depth: impl Into<Option<MaxDepth>>, | ||
targets: TargetKind, | ||
) -> OrganizeResult<Vec<jwalk::DirEntry<((), ())>>> | ||
where | ||
A: AsRef<Path>, | ||
{ | ||
let depth = if let Some(max_depth) = max_depth.into() { | ||
usize::try_from(*max_depth).map_err(WalkerErrorKind::FailedToConvertNumbers)? | ||
} else { | ||
Self::DEFAULT_MAX_DEPTH | ||
}; | ||
|
||
// TODO: Initialize indicatif progress bar | ||
|
||
let files = jwalk::WalkDir::new(path) | ||
.max_depth(depth) | ||
.into_iter() | ||
.flat_map(|f| f.ok()) | ||
.filter(|f| match targets { | ||
TargetKind::Directories => FileType::is_dir(&f.file_type()), | ||
TargetKind::Files => FileType::is_file(&f.file_type()), | ||
TargetKind::Both => true, | ||
}) | ||
.collect_vec(); | ||
|
||
Ok(files) | ||
} | ||
} |
Oops, something went wrong.