From 495d26701592789d9900b32b54a9478f84870d62 Mon Sep 17 00:00:00 2001 From: Elliot Thomas Date: Tue, 7 May 2024 14:03:14 +0100 Subject: [PATCH] add progress bars --- Cargo.lock | 79 ++++++++++++- Cargo.toml | 4 +- src/command/args.rs | 12 +- src/log.rs | 11 +- src/main.rs | 9 +- src/organise/organiser.rs | 241 ++++++++++++++++++++++++++------------ src/organise/settings.rs | 12 +- 7 files changed, 273 insertions(+), 95 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index a864ebc..e9716fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,13 +117,28 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +[[package]] +name = "console" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +dependencies = [ + "encode_unicode", + "lazy_static", + "libc", + "unicode-width", + "windows-sys", +] + [[package]] name = "cyborg" -version = "0.13.0" +version = "0.14.0" dependencies = [ "anyhow", "clap", "env_logger", + "indicatif", + "indicatif-log-bridge", "log", "once_cell", "regex", @@ -132,6 +147,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "encode_unicode" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" + [[package]] name = "env_filter" version = "0.1.0" @@ -167,12 +188,50 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" +[[package]] +name = "indicatif" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +dependencies = [ + "console", + "instant", + "number_prefix", + "portable-atomic", + "unicode-width", +] + +[[package]] +name = "indicatif-log-bridge" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2963046f28a204e3e3fd7e754fd90a6235da05b5378f24707ff0ec9513725ce3" +dependencies = [ + "indicatif", + "log", +] + +[[package]] +name = "instant" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +dependencies = [ + "cfg-if", +] + [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +[[package]] +name = "libc" +version = "0.2.154" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" + [[package]] name = "log" version = "0.4.21" @@ -204,6 +263,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "once_cell" version = "1.19.0" @@ -222,6 +287,12 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +[[package]] +name = "portable-atomic" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" + [[package]] name = "proc-macro2" version = "1.0.81" @@ -422,6 +493,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "unicode-width" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" + [[package]] name = "utf8parse" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 14c2f6b..f6e8c95 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cyborg" -version = "0.13.0" +version = "0.14.0" authors = ["eth0net "] edition = "2021" description = "A simple comic book organisation tool" @@ -14,6 +14,8 @@ categories = ["command-line-utilities"] anyhow = "1.0.80" clap = { version = "4.5.1", features = ["derive"] } env_logger = "0.11.3" +indicatif = "0.17.8" +indicatif-log-bridge = "0.2.2" log = "0.4.21" once_cell = "1.19.0" regex = "1.10.3" diff --git a/src/command/args.rs b/src/command/args.rs index ae37c26..ab4e8d3 100644 --- a/src/command/args.rs +++ b/src/command/args.rs @@ -7,9 +7,9 @@ use std::path::PathBuf; #[group(id = "noisy", multiple = true)] /// Arguments for the application binary. pub struct Args { - /// A list of files or directories to process. + /// A list of files or directories to organise. /// - /// For a directory, each direct child file will be processed. + /// For a directory, each direct child file will be organised. pub paths: Vec, /// Output directory for organised files. @@ -38,9 +38,9 @@ pub struct Args { #[arg(short, long, group = "noisy")] pub dry_run: bool, - /// Stop processing after the first error. + /// Stop organising after the first error. /// - /// If not provided, errors will be logged and processing will continue. + /// If not provided, errors will be logged and organising will continue. #[arg(short, long, default_value = "false")] pub exit: bool, @@ -50,9 +50,9 @@ pub struct Args { #[arg(short, long)] pub force: bool, - /// Recursively process files in subdirectories. + /// Recursively organise files in subdirectories. /// - /// If not provided, only the top-level files will be processed. + /// If not provided, only the top-level files will be organised. #[arg(short, long)] pub recursive: bool, diff --git a/src/log.rs b/src/log.rs index 1aa63a4..5201f8d 100644 --- a/src/log.rs +++ b/src/log.rs @@ -1,9 +1,12 @@ +use anyhow::Context; +use indicatif::MultiProgress; +use indicatif_log_bridge::LogWrapper; use log::LevelFilter; use crate::command::Args; /// Initialize the logger. -pub fn init(args: &Args) { +pub fn init(args: &Args, multibar: MultiProgress) -> anyhow::Result<()> { let mut level = match args.verbose { 0 => LevelFilter::Error, 1 => LevelFilter::Warn, @@ -20,5 +23,9 @@ pub fn init(args: &Args) { level = LevelFilter::Off; } - env_logger::builder().filter_level(level).init(); + let logger = env_logger::builder().filter_level(level).build(); + + LogWrapper::new(multibar, logger) + .try_init() + .context("initializing logger") } diff --git a/src/main.rs b/src/main.rs index 1924109..b0f436c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use clap::Parser; +use indicatif::MultiProgress; use cyborg::command::Args; use cyborg::log; @@ -7,11 +8,13 @@ use cyborg::organise::{Organiser, Settings}; fn main() -> anyhow::Result<()> { let args = Args::parse(); - log::init(&args); + let multibar = MultiProgress::new(); + + log::init(&args, multibar.clone())?; let settings = Settings::from_args(&args); - let processor = Organiser::new(settings); + let organiser = Organiser::new(settings, multibar); - processor.process(args.paths) + organiser.organise(args.paths) } diff --git a/src/organise/organiser.rs b/src/organise/organiser.rs index e680243..ac0291f 100644 --- a/src/organise/organiser.rs +++ b/src/organise/organiser.rs @@ -2,25 +2,41 @@ use std::fs; use std::path::{Path, PathBuf}; use anyhow::Context; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use crate::{comic::Meta, organise::Settings}; #[derive(Default)] -/// Processor for organising comic files +/// Organiser for organising comic files pub struct Organiser { - /// Settings for the processor + /// Settings for the organiser settings: Settings, + /// The progress bar for the organiser + multibar: MultiProgress, } impl Organiser { - /// Create a new Processor instance with the provided settings - pub fn new(settings: Settings) -> Organiser { - Self { settings } + /// Create a new Organiser instance with the provided settings + pub fn new(settings: Settings, multibar: MultiProgress) -> Organiser { + Self { settings, multibar } } - /// Process the provided paths - pub fn process(&self, paths: Vec) -> anyhow::Result<()> { - log::trace!("processing paths"); + /// Organise the provided paths + pub fn organise(&self, paths: Vec) -> anyhow::Result<()> { + log::trace!("organising files"); + + let style = ProgressStyle::default_bar() + .template("{prefix}{msg} [{wide_bar}] [{pos}/{len}] [{duration}]") + .context("setting progress bar style")? + .progress_chars("=> "); + + let pb = self.multibar.add(ProgressBar::new(paths.len() as u64)); + pb.set_style(style.clone()); + + match self.settings.dry_run { + true => pb.set_message("organising files (dry run)"), + false => pb.set_message("organising files"), + } for path in paths { let result = path.metadata(); @@ -29,7 +45,10 @@ impl Organiser { let message = format!("failed to get metadata for: {}", path.display()); log::error!("{message}: {err:#}"); match self.settings.exit { - true => return Err(err).context(message), + true => { + pb.abandon(); + return Err(err).context(message); + } false => continue, } } @@ -37,31 +56,44 @@ impl Organiser { log::trace!("got metadata for: {}", path.display()); let meta = result.unwrap(); + let cpb = self.multibar.insert_after(&pb, ProgressBar::new(0)); + cpb.set_style(style.clone()); + cpb.set_prefix(format!("> {}", cpb.prefix())); + let result = match meta.is_dir() { - true => self.process_dir(&path), - false => self.process_file(&path), + true => self.organise_dir(&path, cpb.clone()), + false => self.organise_file(&path, cpb.clone()), }; if let Err(err) = result { - let message = format!("failed to process path: {}", path.display()); + let message = format!("failed to organise path: {}", path.display()); log::error!("{message}: {err:#}"); match self.settings.exit { - true => return Err(err).context(message), + true => { + pb.abandon(); + return Err(err).context(message); + } false => continue, } } + + pb.inc(1); } - log::trace!("processed paths"); + pb.finish(); + + log::trace!("organised paths"); Ok(()) } } impl Organiser { - /// Process the provided directory - fn process_dir(&self, path: &Path) -> anyhow::Result<()> { - log::debug!("processing dir: {}", path.display()); + /// Organise the provided directory + fn organise_dir(&self, path: &Path, pb: ProgressBar) -> anyhow::Result<()> { + log::debug!("organising dir: {}", path.display()); + + pb.set_message(path.display().to_string()); let directory = path .read_dir() @@ -70,12 +102,17 @@ impl Organiser { log::trace!("read dir: {}", path.display()); for entry in directory { - log::trace!("processing directory entry"); + pb.inc_length(1); + + log::trace!("organising directory entry"); if let Err(err) = entry { let message = format!("failed to read directory entry: {}", path.display()); log::error!("{message}: {err:#}"); match self.settings.exit { - true => return Err(err).context(message), + true => { + pb.abandon(); + return Err(err).context(message); + } false => continue, } } @@ -88,7 +125,10 @@ impl Organiser { let message = format!("failed to get metadata for: {}", path.display()); log::error!("{message}: {err:#}"); match self.settings.exit { - true => return Err(err).context(message), + true => { + pb.abandon(); + return Err(err).context(message); + } false => continue, } } @@ -96,33 +136,44 @@ impl Organiser { log::trace!("got metadata for: {}", path.display()); let meta = result.unwrap(); + let cpb = self.multibar.insert_after(&pb, ProgressBar::new(1)); + cpb.set_style(pb.style()); + cpb.set_prefix(format!(" {}", pb.prefix())); + let result = match [meta.is_dir(), self.settings.recursive] { - [true, true] => self.process_dir(path), + [true, true] => self.organise_dir(path, cpb), [true, false] => { log::trace!("skipping subdirectory: {}", path.display()); continue; } - [false, _] => self.process_file(path), + [false, _] => self.organise_file(path, cpb), }; if let Err(err) = result { - let message = format!("failed to process directory entry: {}", path.display()); + let message = format!("failed to organise directory entry: {}", path.display()); log::error!("{message}: {err:#}"); match self.settings.exit { - true => return Err(err).context(message), + true => { + pb.abandon(); + return Err(err).context(message); + } false => continue, } } + + pb.inc(1) } - log::debug!("processed dir: {}", path.display()); + pb.finish(); + + log::debug!("organised dir: {}", path.display()); Ok(()) } - /// Process the provided file - fn process_file(&self, path: &Path) -> anyhow::Result<()> { - log::debug!("processing file: {}", path.display()); + /// Organise the provided file + fn organise_file(&self, path: &Path, pb: ProgressBar) -> anyhow::Result<()> { + log::debug!("organising file: {}", path.display()); let name = path .file_name() @@ -132,6 +183,8 @@ impl Organiser { log::trace!("old name: {}", name); + pb.set_message(name.to_string()); + let comic: Meta = name.parse()?; let new_name = comic.to_string(); @@ -160,7 +213,7 @@ impl Organiser { _ => log::trace!("output dir exists: {}", output_dir.display()), } - let new_path = output_dir.join(new_name); + let new_path = output_dir.join(new_name.clone()); log::trace!("new path: {}", new_path.display()); @@ -172,6 +225,7 @@ impl Organiser { } [true, false] => { log::warn!("would skip existing file: {}", new_path.display()); + pb.finish_with_message(format!("{}: would skip", pb.message())); return Ok(()); } [false, true] => { @@ -179,6 +233,7 @@ impl Organiser { } [false, false] => { log::warn!("skipping existing file: {}", new_path.display()); + pb.finish_with_message(format!("{}: skipped", pb.message())); return Ok(()); } } @@ -193,15 +248,29 @@ impl Organiser { } [false, true] => { log::info!("moving: {} -> {}", path.display(), new_path.display()); - fs::rename(path, new_path).context("moving file")?; + if let Err(err) = fs::rename(path, new_path).context("moving file") { + log::error!("failed to move file: {}", err); + pb.abandon_with_message(format!("{}: {}", pb.message(), err)); + return Err(err); + } } [false, false] => { log::info!("copying: {} -> {}", path.display(), new_path.display()); - fs::copy(path, new_path).context("copying file")?; + if let Err(err) = fs::copy(path, new_path).context("copying file") { + log::error!("failed to copy file: {}", err); + pb.abandon_with_message(format!("{}: {}", pb.message(), err)); + return Err(err); + } } } - log::debug!("processed file: {}", path.display()); + if log::max_level() >= log::LevelFilter::Info { + pb.finish_with_message(format!("{} -> {}", name, new_name)); + } else { + pb.finish_and_clear(); + } + + log::debug!("organised file: {}", path.display()); Ok(()) } @@ -215,7 +284,7 @@ pub(crate) mod tests { use super::*; #[test] - fn test_process_multiple_paths() { + fn test_organise_multiple_paths() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -235,14 +304,16 @@ pub(crate) mod tests { fs::write(&source_file_1, "").expect("should create first source file"); fs::write(&source_file_2, "").expect("should create second source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_file_1.clone(), source_sub_dir.clone()]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( source_file_1.exists(), @@ -267,7 +338,7 @@ pub(crate) mod tests { } #[test] - fn test_process_recursive() { + fn test_organise_recursive() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -287,15 +358,17 @@ pub(crate) mod tests { fs::write(&source_file_1, "").expect("should create first source file"); fs::write(&source_file_2, "").expect("should create second source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, recursive: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( source_file_1.exists(), @@ -320,7 +393,7 @@ pub(crate) mod tests { } #[test] - fn test_process_non_recursive() { + fn test_organise_non_recursive() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -336,14 +409,16 @@ pub(crate) mod tests { fs::write(&source_file, "").expect("should create second source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( source_file.exists(), @@ -358,7 +433,7 @@ pub(crate) mod tests { } #[test] - fn test_process_move() { + fn test_organise_move() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -371,15 +446,17 @@ pub(crate) mod tests { std::fs::create_dir_all(&output_dir).expect("should create output dir"); fs::write(&source_file, "").expect("should create first source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, move_files: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( !source_file.exists(), @@ -394,7 +471,7 @@ pub(crate) mod tests { } #[test] - fn test_process_exit() { + fn test_organise_exit() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -408,16 +485,18 @@ pub(crate) mod tests { fs::write(&output_dir, contents).expect("should create output file"); fs::write(&source_file_1, contents).expect("should create first source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir.clone(), move_files: true, exit: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_file_1.clone()]; - processor.process(paths).expect_err("should exit early"); + organiser.organise(paths).expect_err("should exit early"); assert!( source_file_1.exists(), @@ -437,7 +516,7 @@ pub(crate) mod tests { } #[test] - fn test_process_force() { + fn test_organise_force() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -453,16 +532,18 @@ pub(crate) mod tests { fs::write(&source_file, contents).expect("should create first source file"); fs::write(&output_file, "").expect("should create first source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, move_files: true, force: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( !source_file.exists(), @@ -482,7 +563,7 @@ pub(crate) mod tests { } #[test] - fn test_process_no_force() { + fn test_organise_no_force() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -498,16 +579,18 @@ pub(crate) mod tests { fs::write(&source_file, contents).expect("should create first source file"); fs::write(&output_file, "").expect("should create first source file"); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, move_files: true, force: false, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( source_file.exists(), @@ -527,7 +610,7 @@ pub(crate) mod tests { } #[test] - fn test_process_series() { + fn test_organise_series() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -545,19 +628,21 @@ pub(crate) mod tests { assert!( !series_dir.exists(), - "series dir should not exist before process: {}", + "series dir should not exist before organise: {}", series_dir.display() ); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir, series: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( source_file.exists(), @@ -577,7 +662,7 @@ pub(crate) mod tests { } #[test] - fn test_process_creates_output_dir() { + fn test_organise_creates_output_dir() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -591,18 +676,20 @@ pub(crate) mod tests { assert!( !output_dir.exists(), - "output dir should not exist before process: {}", + "output dir should not exist before organise: {}", output_dir.display() ); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir.clone(), ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir.clone()]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( output_dir.exists(), @@ -622,7 +709,7 @@ pub(crate) mod tests { } #[test] - fn test_process_dry_run() { + fn test_organise_dry_run() { let dir = TempDir::new().expect("should create temp dir"); let source_dir = dir.child("source"); let output_dir = dir.child("output"); @@ -639,27 +726,29 @@ pub(crate) mod tests { assert!( !output_dir.exists(), - "output dir should not exist before process: {}", + "output dir should not exist before organise: {}", output_dir.display() ); assert!( !series_dir.exists(), - "series dir should not exist before process: {}", + "series dir should not exist before organise: {}", series_dir.display() ); - let processor = Organiser::new(Settings { + let settings = Settings { output: output_dir.clone(), series: true, move_files: true, dry_run: true, force: true, ..Default::default() - }); + }; + + let organiser = Organiser::new(settings, Default::default()); let paths = vec![source_dir.clone()]; - processor.process(paths).expect("should process"); + organiser.organise(paths).expect("should organise"); assert!( !output_dir.exists(), diff --git a/src/organise/settings.rs b/src/organise/settings.rs index 1452379..9bfc4bc 100644 --- a/src/organise/settings.rs +++ b/src/organise/settings.rs @@ -3,9 +3,9 @@ use std::path::PathBuf; use crate::command::Args; #[derive(Default)] -/// Settings for the processor +/// Settings for the organiser pub struct Settings { - /// The output directory for the processed files + /// The output directory for the organised files pub output: PathBuf, /// Whether to output files in series subdirectories pub series: bool, @@ -13,21 +13,21 @@ pub struct Settings { pub move_files: bool, /// Whether to perform a dry run pub dry_run: bool, - /// Whether to exit after processing + /// Whether to exit after organising pub exit: bool, /// Whether to force overwrite of existing files pub force: bool, - /// Whether to process files recursively + /// Whether to organise files recursively pub recursive: bool, } impl Settings { - /// Create a new ProcessorSettings instance with default values + /// Create a new OrganiserSettings instance with default values pub fn new() -> Settings { Settings::default() } - /// Create a new ProcessorSettings instance from the provided Args + /// Create a new OrganiserSettings instance from the provided Args pub fn from_args(args: &Args) -> Settings { Settings { output: args.output.clone(),