Repository: pkolaczk/fclones (language: rust)

Issue:
Apply ordering to `fclones action --dry-run` results
I'm running this in order to test that my `--priority` and `--keep-path` values are right:

```shell
watch 'fclones remove --dry-run < results-file.out'
```

Unfortunately, it's not working because the results are not ordered. So the command is rerun, and the list items dance around every time `watch` updates.

I thought maybe concurrency was affecting it, so I tried `--threads main:1` but that only seems to apply to the `group` action and not the others.

My workaround right now is to run the results through `| sort` but it's not ideal because I need to look for each result in the alphabetical list. 

So my feature request is to add some ordering to the result list, preferably in the same order as the input list. Even if the ordering is not exposed in the CLI options in any way, it would be a help.


Discussion / hints:

Fix patch:
diff --git a/src/dedupe.rs b/src/dedupe.rs
index 376cda9..67646eb 100644
--- a/src/dedupe.rs
+++ b/src/dedupe.rs
@@ -3,9 +3,9 @@
 use std::cmp::{max, min, Reverse};
 use std::collections::HashMap;
 use std::fmt::{Display, Formatter};
-use std::io::{BufWriter, ErrorKind, Write};
+use std::io::{ErrorKind, Write};
 use std::ops::{Add, AddAssign};
-use std::sync::{Arc, Mutex};
+use std::sync::Arc;
 use std::time::SystemTime;
 use std::{fmt, fs, io};
 
@@ -13,7 +13,7 @@ use chrono::{DateTime, FixedOffset, Local};
 use crossbeam_utils::atomic::AtomicCell;
 use rand::distributions::Alphanumeric;
 use rand::Rng;
-use rayon::iter::IntoParallelIterator;
+use rayon::iter::ParallelBridge;
 use rayon::iter::ParallelIterator;
 
 use crate::config::{DedupeConfig, Priority};
@@ -874,14 +874,15 @@ pub fn dedupe<'a, I, P>(
     op: DedupeOp,
     config: &'a DedupeConfig,
     log: &'a Log,
-) -> impl ParallelIterator<Item = FsCommand> + 'a
+) -> impl Iterator<Item = FsCommand> + Send + 'a
 where
-    I: IntoParallelIterator<Item = FileGroup<P>> + 'a,
+    I: IntoIterator<Item = FileGroup<P>> + 'a,
+    I::IntoIter: Send,
     P: Into<Path>,
 {
     let devices = DiskDevices::new(&HashMap::new());
     groups
-        .into_par_iter()
+        .into_iter()
         .flat_map(move |group| match partition(group, config, log) {
             Ok(group) => group.dedupe_script(&op, &devices),
             Err(e) => {
@@ -899,13 +900,14 @@ where
 /// On command execution failure, a warning is logged and the execution of remaining commands
 /// continues.
 /// Returns the number of files processed and the amount of disk space reclaimed.
-pub fn run_script(
-    script: impl IntoParallelIterator<Item = FsCommand>,
-    should_lock: bool,
-    log: &Log,
-) -> DedupeResult {
+pub fn run_script<I>(script: I, should_lock: bool, log: &Log) -> DedupeResult
+where
+    I: IntoIterator<Item = FsCommand>,
+    I::IntoIter: Send,
+{
     script
-        .into_par_iter()
+        .into_iter()
+        .par_bridge()
         .map(|cmd| cmd.execute(should_lock, log))
         .inspect(|res| {
             if let Err(e) = res {
@@ -926,17 +928,15 @@ pub fn run_script(
 /// Returns the number of files processed and the amount of disk space that would be
 /// reclaimed if all commands of the script were executed with no error.
 pub fn log_script(
-    script: impl IntoParallelIterator<Item = FsCommand>,
-    out: impl Write + Send,
+    script: impl IntoIterator<Item = FsCommand>,
+    mut out: impl Write + Send,
 ) -> io::Result<DedupeResult> {
-    let writer = Mutex::new(BufWriter::new(out));
     let err = AtomicCell::new(None);
     let result = script
-        .into_par_iter()
+        .into_iter()
         .map(|cmd| {
-            let mut w = writer.lock().unwrap();
             for line in cmd.to_shell_str() {
-                if let Err(e) = writeln!(w, "{}", line) {
+                if let Err(e) = writeln!(out, "{}", line) {
                     err.store(Some(e));
                     return None;
                 }
@@ -946,8 +946,9 @@ pub fn log_script(
                 reclaimed_space: cmd.space_to_reclaim(),
             })
         })
-        .while_some()
-        .reduce(DedupeResult::default, |a, b| a + b);
+        .take_while(|r| r.is_some())
+        .map(|r| r.unwrap())
+        .fold(DedupeResult::default(), |a, b| a + b);
 
     match err.take() {
         None => Ok(result),
diff --git a/src/main.rs b/src/main.rs
index a89b336..6502865 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,7 +9,6 @@ use std::{fs, io};
 use console::style;
 use fallible_iterator::FallibleIterator;
 use itertools::Itertools;
-use rayon::iter::ParallelBridge;
 use regex::Regex;
 use structopt::StructOpt;
 
@@ -213,8 +212,7 @@ pub fn run_dedupe(op: DedupeOp, config: DedupeConfig, log: &mut Log) -> Result<(
         })
         .take_while(|g| g.is_some())
         .map(|g| g.unwrap())
-        .inspect(|_| progress.tick())
-        .par_bridge();
+        .inspect(|_| progress.tick());
 
     let upto = if op == DedupeOp::RefLink {
         // Can't be sure because any previous deduplications are not


Test patch: