Skip to content

Commit

Permalink
feat(actors): split the pipeline into several actors that could be ru…
Browse files Browse the repository at this point in the history
…n in parallel if needed
  • Loading branch information
simonsan committed Jun 5, 2023
1 parent 859f50f commit 27f0ad6
Show file tree
Hide file tree
Showing 12 changed files with 344 additions and 259 deletions.
7 changes: 7 additions & 0 deletions crates/organize-rs_core/src/actors.rs
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 {}
1 change: 1 addition & 0 deletions crates/organize-rs_core/src/actors/action_applicator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions crates/organize-rs_core/src/actors/conflict_handler.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

138 changes: 138 additions & 0 deletions crates/organize-rs_core/src/actors/filter_applicator.rs
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)
}
}
142 changes: 142 additions & 0 deletions crates/organize-rs_core/src/actors/location_walker.rs
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)
}
}
Loading

0 comments on commit 27f0ad6

Please sign in to comment.