From 37aad75d242f73091b3fc1058db28c40bbe6da70 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 5 Jun 2023 19:44:32 +0200 Subject: [PATCH] feat(preview): implement previewing an action --- .cargo/config.toml | 2 +- .vscode/launch.json | 17 +--- config/empty.yaml | 18 ++++ crates/organize-rs_core/src/actions.rs | 12 +++ crates/organize-rs_core/src/actions/impl_.rs | 8 ++ .../src/actors/action_applicator.rs | 11 +++ .../src/actors/location_walker.rs | 6 +- crates/organize-rs_core/src/lib.rs | 11 +-- crates/organize-rs_core/src/runner.rs | 96 +++++++++++++++---- crates/organize-rs_core/src/state.rs | 72 ++++++++------ src/commands/run/config.rs | 26 ++++- 11 files changed, 201 insertions(+), 78 deletions(-) create mode 100644 config/empty.yaml diff --git a/.cargo/config.toml b/.cargo/config.toml index 1679878..a334953 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -2,7 +2,7 @@ # TODO: Activate `deny warnings` # rustflags = "-C target-cpu=native -D warnings" rustflags = "-C target-cpu=native" -incremental = true +# incremental = true # TODO: Introduced for `cargo public-api`, # remove when it's working on sparse registry diff --git a/.vscode/launch.json b/.vscode/launch.json index 22196c8..a1d8d8e 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -226,19 +226,10 @@ } }, "args": [ - "filter", - "empty", - "-r", - "-m", - "4", - "-l", - "C:\\Users\\dailyuse\\dev-src\\organize\\tests", - "--ignore-path", - ".git", - "--ignore-name", - "toml", - "--ignore-path", - "fixtures" + "run", + "config", + "--paths", + "config\\empty.yaml", ], "cwd": "${workspaceFolder}" }, diff --git a/config/empty.yaml b/config/empty.yaml new file mode 100644 index 0000000..4e805a0 --- /dev/null +++ b/config/empty.yaml @@ -0,0 +1,18 @@ +rules: + - name: Recursively trash empty entries + enabled: true + locations: + - !recursive + path: C:\Users\dailyuse\dev-src\ + max_depth: 10 + target: both + filter_groups: + - filters: + - !empty + results: include + match: all + actions: + - mode: preview + action: !trash + tags: + - !custom Test::Filter::EmptyEntries diff --git a/crates/organize-rs_core/src/actions.rs b/crates/organize-rs_core/src/actions.rs index 043ff57..d250794 100644 --- a/crates/organize-rs_core/src/actions.rs +++ b/crates/organize-rs_core/src/actions.rs @@ -26,6 +26,8 @@ type ActionClosure<'a, C> = pub enum ActionResultKind { /// A preview of a to be executed action Preview { + /// message to be printed + msg: String, /// file or directory path the action should be executed on path: PathBuf, /// corresponding action @@ -67,6 +69,16 @@ pub enum ActionApplicationKind { UserInput, } +impl ActionApplicationKind { + /// Returns `true` if the action application kind is [`Preview`]. + /// + /// [`Preview`]: ActionApplicationKind::Preview + #[must_use] + pub fn is_preview(&self) -> bool { + matches!(self, Self::Preview) + } +} + #[derive(Debug, Clone, Deserialize, Serialize, Display)] pub struct ActionContainer { pub mode: ActionApplicationKind, diff --git a/crates/organize-rs_core/src/actions/impl_.rs b/crates/organize-rs_core/src/actions/impl_.rs index 26d8402..c1d0832 100644 --- a/crates/organize-rs_core/src/actions/impl_.rs +++ b/crates/organize-rs_core/src/actions/impl_.rs @@ -56,6 +56,7 @@ impl ActionKind { fn action_no_action(&self) -> ActionClosure { Box::new(|entry, _preview| { Ok(ActionResultKind::Preview { + msg: format!("(Preview) No action: {}", entry.path().display()), path: entry.path(), action: self.to_owned(), }) @@ -66,6 +67,7 @@ impl ActionKind { Box::new(|entry, preview| { if preview { Ok(ActionResultKind::Preview { + msg: format!("(Preview) Trash: {}", entry.path().display()), path: entry.path(), action: self.to_owned(), }) @@ -81,6 +83,7 @@ impl ActionKind { Box::new(|entry, preview| { if preview { Ok(ActionResultKind::Preview { + msg: format!("(Preview) Delete: {}", entry.path().display()), path: entry.path(), action: self.to_owned(), }) @@ -96,6 +99,11 @@ impl ActionKind { Box::new(move |entry, preview| { if preview { Ok(ActionResultKind::Preview { + msg: format!( + "(Preview) Symlink: {} -> {}", + dst.display(), + entry.path().display() + ), path: entry.path(), action: self.to_owned(), }) diff --git a/crates/organize-rs_core/src/actors/action_applicator.rs b/crates/organize-rs_core/src/actors/action_applicator.rs index 8b13789..1c9b9ab 100644 --- a/crates/organize-rs_core/src/actors/action_applicator.rs +++ b/crates/organize-rs_core/src/actors/action_applicator.rs @@ -1 +1,12 @@ +use crate::actions::ActionApplicationCollection; +#[derive(Debug, Default)] +pub struct ActionApplicator { + actions: ActionApplicationCollection, +} + +impl ActionApplicator { + pub fn new(actions: ActionApplicationCollection) -> Self { + ActionApplicator { actions } + } +} diff --git a/crates/organize-rs_core/src/actors/location_walker.rs b/crates/organize-rs_core/src/actors/location_walker.rs index 8519d0a..a0ce80a 100644 --- a/crates/organize-rs_core/src/actors/location_walker.rs +++ b/crates/organize-rs_core/src/actors/location_walker.rs @@ -1,6 +1,6 @@ use itertools::Itertools; use jwalk::{ClientState, DirEntry}; -use std::{fmt::Display, fs::FileType, path::Path, vec::IntoIter}; +use std::{fmt::Display, fs::FileType, path::Path, slice::Iter, vec::IntoIter}; use crate::{ error::{OrganizeResult, WalkerErrorKind}, @@ -32,6 +32,10 @@ impl DirEntryData { }); println!("Total entry count: {count}"); } + + pub(crate) fn iter(&self) -> Iter<'_, jwalk::DirEntry<((), ())>> { + self.0.iter() + } } impl Display for DirEntryData { diff --git a/crates/organize-rs_core/src/lib.rs b/crates/organize-rs_core/src/lib.rs index c2a3a7d..3714dfe 100644 --- a/crates/organize-rs_core/src/lib.rs +++ b/crates/organize-rs_core/src/lib.rs @@ -1,4 +1,5 @@ pub mod actions; +pub mod actors; pub mod aliases; pub mod concurrency; pub mod config; @@ -11,15 +12,5 @@ pub mod py_config; pub mod rules; pub mod runner; pub mod ser_de; -pub mod actors; pub mod state; pub mod tags; - - - - - - - - - diff --git a/crates/organize-rs_core/src/runner.rs b/crates/organize-rs_core/src/runner.rs index 36e0f98..08d6a06 100644 --- a/crates/organize-rs_core/src/runner.rs +++ b/crates/organize-rs_core/src/runner.rs @@ -3,9 +3,14 @@ use std::{collections::HashSet, path::Path}; use itertools::Itertools; use crate::{ + actions::{ActionApplicationKind, ActionResultKind}, actors::{filter_applicator::FilterApplicator, location_walker::LocationWalker}, config::OrganizeConfig, - state::{HandleConflicts, Init, Inspect, ProcessingState, Start}, + error::OrganizeResult, + state::{ + ActionApplication, ActionPreview, ConflictHandling, Filtering, Initialize, Inspection, + ProcessingStage, + }, tags::{Tag, TagCollection}, }; @@ -13,29 +18,29 @@ use std::iter::FromIterator; pub struct Runner where - S: ProcessingState, + S: ProcessingStage, { configs: Vec, extra: S, } -impl Runner { - pub fn load_configs(paths: &[impl AsRef]) -> Runner { +impl Runner { + pub fn load_configs(paths: &[impl AsRef]) -> Runner { let mut configs = vec![]; - paths.into_iter().for_each(|path| { + paths.iter().for_each(|path| { let config = OrganizeConfig::load_from_file(path); configs.push(config); }); - Runner:: { + Runner:: { configs, - extra: Start::default(), + extra: Filtering::default(), } } } -impl Runner { - pub fn apply_filters(self, tags: Vec) -> Runner { +impl Runner { + pub fn apply_filters(self, tags: Vec) -> Runner { let mut entries = vec![]; self.configs.iter().for_each(|config| { config.rules().iter().for_each(|rule| { @@ -59,9 +64,9 @@ impl Runner { }) }); - Runner:: { + Runner:: { configs: self.configs, - extra: Inspect::with_entries(entries), + extra: Inspection::with_entries(entries), } } @@ -77,20 +82,73 @@ impl Runner { } } -impl Runner { - pub fn handle_conflicts(self) -> Runner { - let entries = self.extra.entries(); - - Runner:: { +impl Runner { + pub fn finish_inspection(self) -> Runner { + Runner:: { configs: self.configs, - extra: HandleConflicts::with_entries(entries), + extra: ActionPreview::with_entries(self.extra.entries()), } } - pub fn inspect_entries(self) -> Runner { + pub fn inspect_entries(self) -> Runner { self.extra.print_entries(); self } } -impl Runner {} +impl Runner { + pub fn preview_actions(self) -> OrganizeResult<()> { + // * if action::mode == `is_preview()` we return Runner + // * and only println!() what an action might do + // * else we continue to Runner + self.extra.entries().iter().for_each(|(rule, entry)| { + rule.actions().iter().for_each(|action_container| { + entry.iter().for_each(|entry| { + match action_container.action.get_action()( + entry, + true, // * is always true, as it's preview + // ! for application we can use: + // ! `action_container.mode.is_preview()` + ) { + Ok(ActionResultKind::Preview { + msg, + path: _, + action: _, + }) => println!("{msg}"), + Err(err) => eprintln!("{err}"), + _ => (), + } + }) + }) + }); + Ok(()) + } + + pub fn ask_confirmation(self) -> OrganizeResult> { + todo!() + } +} + +impl Runner { + pub fn apply_actions(self) -> OrganizeResult> { + todo!() + } + + pub fn check_conflicts(self) -> Runner { + todo!() + } +} + +// pub fn handle_conflicts(self) -> Runner { +// let entries = self.extra.entries(); + +// Runner:: { +// configs: self.configs, +// extra: HandleConflicts::with_entries(entries), +// } +// } +impl Runner { + pub fn view_conflicts(self) -> Runner { + todo!() + } +} diff --git a/crates/organize-rs_core/src/state.rs b/crates/organize-rs_core/src/state.rs index 4281f74..9e94c40 100644 --- a/crates/organize-rs_core/src/state.rs +++ b/crates/organize-rs_core/src/state.rs @@ -4,23 +4,28 @@ use jwalk::DirEntry; use crate::{ - rules::Rule, - actors::location_walker::{DirEntryData}, + actions::conflicts::OnConflictKind, actors::location_walker::DirEntryData, rules::Rule, }; // States #[derive(Debug, Clone, Copy, Default)] -pub struct Init; +pub struct Initialize; #[derive(Debug, Clone, Copy, Default)] -pub struct Start; +pub struct Filtering; #[derive(Debug, Default)] -pub struct Inspect { +pub struct Inspection { entries: Vec<(Rule, DirEntryData)>, } -impl Inspect { +#[derive(Debug, Default)] +pub struct ConflictHandling { + entries: Vec<(Rule, DirEntryData)>, + conflicts: Vec>, +} + +impl Inspection { pub fn with_entries(entries: Vec<(Rule, DirEntryData)>) -> Self { Self { entries } } @@ -37,40 +42,49 @@ impl Inspect { } } +impl ConflictHandling { + pub fn with_entries(entries: Vec<(Rule, DirEntryData)>) -> Self { + Self { + entries, + conflicts: vec![], + } + } +} + #[derive(Debug, Default)] -pub struct HandleConflicts { +pub struct AskConfirmation; + +#[derive(Debug, Default)] +pub struct ActionPreview { entries: Vec<(Rule, DirEntryData)>, - conflicts: Vec>, + conflicts: Option>, } -impl HandleConflicts { +impl ActionPreview { pub fn with_entries(entries: Vec<(Rule, DirEntryData)>) -> Self { Self { entries, - conflicts: vec![], + conflicts: None, } } + pub fn entries(self) -> Vec<(Rule, DirEntryData)> { + self.entries + } } -#[derive(Debug, Clone, Copy, Default)] -pub struct AskConfirmation; +#[derive(Debug, Default)] +pub struct ActionApplication; -#[derive(Debug, Clone, Copy, Default)] -pub struct Preview; +#[derive(Debug, Clone, Default)] +pub struct Reporting; -#[derive(Debug, Clone, Copy, Default)] -pub struct ApplyActions; +pub trait ProcessingStage {} -#[derive(Debug, Clone, Copy, Default)] -pub struct Report; - -pub trait ProcessingState {} - -impl ProcessingState for Init {} -impl ProcessingState for Start {} -impl ProcessingState for Inspect {} -impl ProcessingState for HandleConflicts {} -impl ProcessingState for AskConfirmation {} -impl ProcessingState for Preview {} -impl ProcessingState for ApplyActions {} -impl ProcessingState for Report {} +impl ProcessingStage for Initialize {} +impl ProcessingStage for Filtering {} +impl ProcessingStage for Inspection {} +impl ProcessingStage for ActionPreview {} +impl ProcessingStage for AskConfirmation {} +impl ProcessingStage for ActionApplication {} +impl ProcessingStage for ConflictHandling {} +impl ProcessingStage for Reporting {} diff --git a/src/commands/run/config.rs b/src/commands/run/config.rs index fc516ec..d2eb144 100644 --- a/src/commands/run/config.rs +++ b/src/commands/run/config.rs @@ -6,7 +6,7 @@ use abscissa_core::{status_err, Application, Command, Runnable}; use anyhow::Result; use clap::Args; -use organize_rs_core::{runner::Runner, state::Init, tags::Tag}; +use organize_rs_core::{runner::Runner, state::Initialize, tags::Tag}; use crate::application::ORGANIZE_APP; @@ -24,12 +24,28 @@ pub struct RunConfigCmd { impl RunConfigCmd { fn inner_run(&self) -> Result<()> { - let runner = Runner::::load_configs(&self.paths) + let runner = Runner::::load_configs(&self.paths) .apply_filters(self.tags.clone()) .inspect_entries() - .handle_conflicts(); - // let runner: Runner = runner.get_confirmation(); - // let runner: Runner = runner.apply_actions(); + .finish_inspection() + .preview_actions()?; + // .ask_confirmation()? + // .apply_actions()?; + + // ? Conflict handling + // * Probably done in a loop until all the conflicts are handled + // * loop can be interrupted + // * should jump to report of actions + // loop { + // runner + // .check_conflicts() + // .view_conflicts() + // .preview_actions() + // .ask_confirmation()? + // .apply_actions()? + // } + + // runner.print_report(); Ok(()) } }