From ad4ffbb5a10bbc6911de1dc57ec269151917d619 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 5 Jun 2023 21:18:24 +0200 Subject: [PATCH 01/12] docs(todo): add some thoughts about aliasing --- TODO.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/TODO.md b/TODO.md index 4b377b9..ae450c9 100644 --- a/TODO.md +++ b/TODO.md @@ -10,6 +10,13 @@ - check against py-organize documentation for feature parity 1. implement unit tests for actions and the actions themselves 1. implement `organize run config` with preview and destructive run +1. implement `Alias` being just defined via `Vec` and used with the templating syntax `{{alias_name}}` + - check places where it is applicable + - theoretically everywhere, where we take a `PathBuf` or `String` as part of a `Path` + - e.g. extensions, locations, ignore_name, ignore_path + - not usable in actions, or good error handling + - e.g. someone could use an alias for a `move::dst`, which would essentially copy the file to these places + - this should error out and actually be able to be checked beforehand, e.g. don't accept an alias that references more than one location string ## Features From e97b7ffc9b6b874bea4ca587a2eaf6c97a0c6478 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Mon, 5 Jun 2023 22:02:24 +0200 Subject: [PATCH 02/12] chore: update dependencies and clippy fixes --- Cargo.lock | 64 +++++++++++++++--------------- Cargo.toml | 10 ++--- TODO.md | 1 + crates/organize-rs_core/Cargo.toml | 4 +- src/commands/filter.rs | 5 +-- 5 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d7b8445..33f9375 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -71,9 +71,9 @@ dependencies = [ [[package]] name = "aho-corasick" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67fc08ce920c31afb70f013dcce1bfc3a3195de6a228474e45e1f145b36f8d04" +checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] @@ -232,9 +232,9 @@ checksum = "e6e9e01327e6c86e92ec72b1c798d4a94810f147209bbe3ffab6a86954937a6f" [[package]] name = "cargo-manifest" -version = "0.8.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b553708c7ce3d7789774f44389865719c2901afb5e6b3ae8cd1303c95488735" +checksum = "a4165e1fb52e9c7d9196d9c591af9d82dee1aa643d709e1ff51bc8e83c25c0a9" dependencies = [ "serde", "toml 0.7.4", @@ -304,9 +304,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93aae7a4192245f70fe75dd9157fc7b4a5bf53e88d30bd4396f7d8f9284d5acc" +checksum = "401a4694d2bf92537b6867d94de48c4842089645fdcdf6c71865b175d836e9c2" dependencies = [ "clap_builder", "clap_derive", @@ -315,9 +315,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f423e341edefb78c9caba2d9c7f7687d0e72e89df3ce3394554754393ac3990" +checksum = "72394f3339a76daf211e57d4bcb374410f3965dcc606dd0e03738c7888766980" dependencies = [ "anstream", "anstyle", @@ -329,18 +329,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.3.0" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a04ddfaacc3bc9e6ea67d024575fafc2a813027cf374b8f24f7bc233c6b6be12" +checksum = "7f6b5c519bab3ea61843a7923d074b04245624bb84a64a8c150f5deb014e388b" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.3.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "191d9573962933b4027f932c600cd252ce27a8ad5979418fe78e43c07996f27b" +checksum = "b8cd2b2a819ad6eec39e8f1d6b53001af1e5469f8c177579cdaeb313115b825f" dependencies = [ "heck", "proc-macro2", @@ -692,9 +692,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" [[package]] name = "form_urlencoded" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9c384f161156f5260c24a097c56119f9be8c798586aecc13afbcbe7b7e26bf8" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" dependencies = [ "percent-encoding", ] @@ -899,9 +899,9 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14ddfc70884202db2244c223200c204c2bda1bc6e0998d11b5e024d657209e6" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" dependencies = [ "unicode-bidi", "unicode-normalization", @@ -926,9 +926,9 @@ dependencies = [ [[package]] name = "indicatif" -version = "0.17.4" +version = "0.17.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db45317f37ef454e6519b6c3ed7d377e5f23346f0823f86e65ca36912d1d0ef8" +checksum = "8ff8cc23a7393a397ed1d7f56e6365cba772aba9f9912ab968b03043c395d057" dependencies = [ "console", "instant", @@ -1055,9 +1055,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "fc86cde3ff845662b8f4ef6cb50ea0e20c524eb3d29ae048287e06a1b3fa6a81" [[package]] name = "linked-hash-map" @@ -1180,9 +1180,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.17.2" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9670a07f94779e00908f3e686eab508878ebb390ba6e604d3a284c00e8d0487b" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" [[package]] name = "open" @@ -1322,9 +1322,9 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "percent-encoding" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478c572c3d73181ff3c2539045f6eb99e5491218eae919370993b890cdbdd98e" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" [[package]] name = "pin-project-lite" @@ -1486,9 +1486,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.8.3" +version = "1.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81ca098a9821bd52d6b24fd8b10bd081f47d39c22778cafaa75a2857a62c6390" +checksum = "d0ab3ca65655bb1e41f2a8c8cd662eb4fb035e67c3f78da1d61dffe89d07300f" dependencies = [ "aho-corasick", "memchr", @@ -1608,9 +1608,9 @@ dependencies = [ [[package]] name = "rustdoc-json" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d5918941537d11089c8587d8f271c47ebffbde39c6f85b286820f130769260e" +checksum = "2b40317291158879e09b2e5343f13a95bfb10a0e0e61eb68da4e962612f066bd" dependencies = [ "cargo-manifest", "cargo_metadata", @@ -1644,9 +1644,9 @@ dependencies = [ [[package]] name = "rustup-toolchain" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4d51c4ed26a5c04c5474585c9909d8b1ab90d31923c6a383832b70dbbf70da9" +checksum = "2b5bac30d8979e3ac19bb06339d5eed5ccd8bee1ee5fdbec1d3964fb5ceac21e" dependencies = [ "thiserror", ] @@ -2157,9 +2157,9 @@ checksum = "1865806a559042e51ab5414598446a5871b561d21b6764f2eabb0dd481d880a6" [[package]] name = "url" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" +checksum = "50bff7831e19200a85b17131d085c25d7811bc4e186efdaf54bbd132994a88cb" dependencies = [ "form_urlencoded", "idna", diff --git a/Cargo.toml b/Cargo.toml index 3c466cf..3645bb5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -38,13 +38,13 @@ organize-rs_core = { version = "0.2.0", path = "crates/organize-rs_core", featur "cli", ] } organize-rs_testing = { path = "crates/organize-rs_testing" } -clap = { version = "4.3.0", features = ["derive", "env", "wrap_help"] } +clap = { version = "4.3.2", features = ["derive", "env", "wrap_help"] } directories = "5.0.1" console = { version = "0.15.7", features = ["windows-console-colors"] } thiserror = "1.0.40" anyhow = "1.0.71" -once_cell = "1.17.2" +once_cell = "1.18.0" duct = "0.13.6" chrono = { version = "0.4.26", default-features = false, features = [ "serde", @@ -54,13 +54,13 @@ chrono = { version = "0.4.26", default-features = false, features = [ ] } jwalk = "0.8.1" -regex = "1.8.3" +regex = "1.8.4" abscissa_core = "0.7.0" itertools = "0.10.5" displaydoc = "0.2.4" -clap_complete = "4.3.0" -indicatif = "0.17.4" +clap_complete = "4.3.1" +indicatif = "0.17.5" rayon = "1.7.0" trash = "3.0.2" dialoguer = "0.10.4" diff --git a/TODO.md b/TODO.md index ae450c9..456de7e 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,7 @@ ## Next +1. update screenshots and Readme.md 1. change arguments to some filters to be optional - so they return a broad range of information, that can be used to `echo` - or rather create a new `info`/`inspect` filter for that diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index e3e10d0..439349d 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -72,8 +72,8 @@ serde_with = { workspace = true } console = { workspace = true } [dev-dependencies] -rustup-toolchain = "0.1.4" -rustdoc-json = "0.8.5" +rustup-toolchain = "0.1.5" +rustdoc-json = "0.8.6" public-api = "0.31.0" expect-test = "1.4.1" rstest = { workspace = true } diff --git a/src/commands/filter.rs b/src/commands/filter.rs index b8b40a5..cd86579 100644 --- a/src/commands/filter.rs +++ b/src/commands/filter.rs @@ -4,14 +4,13 @@ use std::path::PathBuf; use abscissa_core::{Command, Runnable}; use clap::Args; -use itertools::Itertools; + use organize_rs_core::{ - actors::{filter_applicator::FilterApplicator, location_walker::LocationWalker}, filters::{ FilterApplicationKind, FilterGroup, FilterGroupCollection, FilterGroupOperationKind, FilterKind, RecursiveFilterArgs, }, - locations::{LocationCollection, LocationKind, MaxDepth, TargetKind}, + locations::{TargetKind}, }; /// `filter` subcommand From 3bb642bc106a7fd25382e75b00ca0f5a63b90bb5 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 7 Jun 2023 00:46:59 +0200 Subject: [PATCH 03/12] feat(templating): start implementing parser for template strings --- Cargo.lock | 1 - Cargo.toml | 1 - TODO.md | 30 ++++- config/links_desktop.yaml | 22 ++++ crates/organize-rs_core/Cargo.toml | 4 +- .../examples/templating_parser.rs | 87 +++++++++++++++ .../organize-rs_core/examples/yaml_parser.rs | 21 ---- crates/organize-rs_core/src/actions.rs | 39 +------ crates/organize-rs_core/src/actions/impl_.rs | 12 +- crates/organize-rs_core/src/filters.rs | 2 +- crates/organize-rs_core/src/lib.rs | 1 + crates/organize-rs_core/src/templating.rs | 104 ++++++++++++++++++ src/commands/generate/gen_config.rs | 12 +- src/commands/run/config.rs | 7 +- src/commands/run/script.rs | 1 + src/lib.rs | 5 +- 16 files changed, 265 insertions(+), 84 deletions(-) create mode 100644 config/links_desktop.yaml create mode 100644 crates/organize-rs_core/examples/templating_parser.rs delete mode 100644 crates/organize-rs_core/examples/yaml_parser.rs create mode 100644 crates/organize-rs_core/src/templating.rs diff --git a/Cargo.lock b/Cargo.lock index 33f9375..6f16d55 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1209,7 +1209,6 @@ dependencies = [ "chrono", "clap", "clap_complete", - "console", "dialoguer", "directories", "duct", diff --git a/Cargo.toml b/Cargo.toml index 3645bb5..a28f652 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -174,7 +174,6 @@ dialoguer = { workspace = true } ron = { workspace = true } open = { workspace = true } serde_with = { workspace = true } -console = { workspace = true } [dev-dependencies] diff --git a/TODO.md b/TODO.md index 456de7e..f0a71e8 100644 --- a/TODO.md +++ b/TODO.md @@ -3,15 +3,34 @@ ## Next 1. update screenshots and Readme.md -1. change arguments to some filters to be optional - - so they return a broad range of information, that can be used to `echo` - - or rather create a new `info`/`inspect` filter for that - - which we can pass as an argument, which attribute we want to inspect +1. feat(templating): change arguments of some filters to be optional + - [APPROACH1] FilterKind::Inspect/FilterKind::Template + - and give information which template information we want to use (though we would still need to change return of `FilterClosures`) + - [APPROACH2] use `FilterKind` to lookup available template strings + - these can then be used in `actions` as e.g. `{{entry.size}}` + - lookup table: `[key: FilterKind -> available_templates: Vec]` 1. generate example configs for integration test cases - check against py-organize documentation for feature parity 1. implement unit tests for actions and the actions themselves 1. implement `organize run config` with preview and destructive run 1. implement `Alias` being just defined via `Vec` and used with the templating syntax `{{alias_name}}` + + ```yaml + aliases: + - !my_music_folders + paths: + - path1 + - path2 + - etc + ``` + + and then something like + + ```yaml + locations: + - {{my_music_folders}} + ``` + - check places where it is applicable - theoretically everywhere, where we take a `PathBuf` or `String` as part of a `Path` - e.g. extensions, locations, ignore_name, ignore_path @@ -27,12 +46,13 @@ - .ini 1. implement file name templating - - e.g. with or - or bare with `.replace` and own placeholders like `{{file_name}}`, `{{counter}}` etc. - support batch renaming (e.g. rename all images in a directory to image_n where n is a number - remove some prefix from filenames - we want to have filename tags as well e.g. `dnt_` (do not touch) or `wip_` so these things are treated differently - also we want to have `{project_name}_` that we can remove after moving to a project folder + - do we really want to remove that? maybe give that as an option + `remove_keyword: bool` 1. implement Terminal UI - generate config interactively `generate config --interactive` diff --git a/config/links_desktop.yaml b/config/links_desktop.yaml new file mode 100644 index 0000000..456145e --- /dev/null +++ b/config/links_desktop.yaml @@ -0,0 +1,22 @@ +rules: + - name: Move links on Desktop to Folder + enabled: true + locations: + - !recursive + path: C:\Users\dailyuse\Desktop\ + max_depth: 2 + target: files + filter_groups: + - filters: + - !extension + exts: + - lnk + results: include + match: all + actions: + - mode: preview + action: !move + dst: C:\Users\dailyuse\Desktop\{{uppercase(extension)}}\ + on_conflict: skip + tags: + - !custom Test::LinksOnDesktop diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index 439349d..af71a1e 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -30,8 +30,8 @@ harness = true edition = "2021" [[example]] -name = "yaml_parser" -path = "examples/yaml_parser.rs" +name = "templating_parser" +path = "examples/templating_parser.rs" test = true bench = true doc = true diff --git a/crates/organize-rs_core/examples/templating_parser.rs b/crates/organize-rs_core/examples/templating_parser.rs new file mode 100644 index 0000000..53f5ee6 --- /dev/null +++ b/crates/organize-rs_core/examples/templating_parser.rs @@ -0,0 +1,87 @@ +use winnow::{ + ascii::alpha0, + combinator::alt, + error::{Error, ErrorKind}, + multi::separated0, + sequence::{delimited, preceded}, + token::take_while, + IResult, Parser, +}; +use winnow::{ + stream::AsChar, + token::{any, tag}, +}; + +pub fn parse_text_incl_underscore_hyphen(input: &str) -> IResult<&str, &str> { + take_while(1.., (AsChar::is_alpha, ('_'), ('-'))).parse_next(input) +} + +pub fn parse_boundary(input: &str) -> IResult<&str, &str> { + take_while(1.., "{()}").parse_next(input) +} + +pub fn parse_single_template(input: &str) -> IResult<&str, &str> { + let (remainder, (_, text, _)) = + (parse_boundary, parse_template_content, parse_boundary).parse_next(input)?; + Ok((remainder, text)) +} + +pub fn parse_transform_template(input: &str) -> IResult<&str, (&str, Vec<&str>)> { + let (_, template) = parse_single_template.parse_next(input)?; + + let (remainder, (text, _, arg, _)) = ( + parse_text_incl_underscore_hyphen, + parse_boundary, + parse_separated_text, + parse_boundary, + ) + .parse_next(template)?; + Ok((remainder, (text, arg))) +} + +pub fn parse_dotted_template(input: &str) -> IResult<&str, Vec<&str>> { + let (_, template) = parse_single_template.parse_next(input)?; + + (parse_separated_text).parse_next(template) +} + +fn parse_separated_text(input: &str) -> IResult<&str, Vec<&str>> { + // take_while(1.., (AsChar::is_alpha, ('.'))).parse_next(input) + separated0(parse_text_incl_underscore_hyphen, '.').parse_next(input) +} + +fn parse_template_content(input: &str) -> IResult<&str, &str> { + take_while(1.., (AsChar::is_alpha, ('.'), ('_'), ('('), (')'))).parse_next(input) +} + +// pub fn parse_transform_template_with_sub_key(input: &str) -> IResult<&str, (&str, Vec<&str>)> { +// let (remainder, (function, arg)) = parse_transform_template.parse_next(input)?; +// let (_, args) = parse_arg_dot.parse_next(arg)?; +// Ok((remainder, (function, args))) +// } + +fn main() { + let case1 = "{{extension}}"; + let case2 = "{{uppercase(extension)}}"; + let case3 = "{{lowercase(entry.name)}}"; + let case4 = "{{entry.created.year}}"; + let case5 = "{{file_content.customer}}"; + let case6 = "{{entry.metadata.last_modified.year}}"; + let case7 = "{{size.traditional}} -- {{relative_path}}"; + let case8 = "{{date_added.strftime('%Y-%m-%d')}}"; + + let (remainder, result) = (parse_single_template).parse_next(case1).unwrap(); + assert_eq!(result, ("extension")); + let (remainder, result) = (parse_transform_template).parse_next(case2).unwrap(); + assert_eq!(result, ("uppercase", vec!["extension"])); + let (remainder, result) = (parse_transform_template).parse_next(case3).unwrap(); + assert_eq!(result, ("lowercase", vec!["entry", "name"])); + let (remainder, result) = (parse_dotted_template).parse_next(case4).unwrap(); + assert_eq!(result, (vec!["entry", "created", "year"])); + let (remainder, result) = (parse_dotted_template).parse_next(case5).unwrap(); + assert_eq!(result, (vec!["file_content", "customer"])); + let (remainder, result) = (parse_dotted_template).parse_next(case6).unwrap(); + assert_eq!(result, (vec!["entry", "metadata", "last_modified", "year"])); + + println!("{remainder} - {result:?}"); +} diff --git a/crates/organize-rs_core/examples/yaml_parser.rs b/crates/organize-rs_core/examples/yaml_parser.rs deleted file mode 100644 index a4edf25..0000000 --- a/crates/organize-rs_core/examples/yaml_parser.rs +++ /dev/null @@ -1,21 +0,0 @@ -use organize_rs_core::rules::Rule; - -pub fn main() { - let rule = r#" -- name: "Sort my invoices and receipts" - locations: ~/Downloads - subfolders: true - filters: - - extension: pdf - - name: - contains: - - Invoice - - Order - - Purchase - case_sensitive: false - actions: - - move: ~/Documents/Shopping/ -"#; - - println!("{:?}", serde_yaml::from_str::(rule).unwrap()); -} diff --git a/crates/organize-rs_core/src/actions.rs b/crates/organize-rs_core/src/actions.rs index 67c6934..fd9c036 100644 --- a/crates/organize-rs_core/src/actions.rs +++ b/crates/organize-rs_core/src/actions.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use crate::{ actions::conflicts::{ConflictKind, ConflictResolutionKind}, - error::OrganizeError, + error::OrganizeError, templating::TemplateStringKind, }; type ActionClosure<'a, C> = @@ -185,43 +185,6 @@ impl Default for MacOsTagColourKind { } } -/// Support template strings -#[cfg_attr(feature = "cli", derive(ValueEnum))] -#[derive(Debug, Clone, Copy, Deserialize, Serialize, Display)] -pub enum TemplateStringKind { - /// {{filename}} - Filename, - /// {{counter}} - Counter, - /// {{extension}} - Extension, -} - -impl TemplateStringKind { - /// Returns `true` if [`TemplateStrings`] is [`Filename`]. - /// - /// [`Filename`]: TemplateStrings::Filename - #[must_use] - pub fn is_filename(&self) -> bool { - matches!(self, Self::Filename) - } - - /// Returns `true` if [`TemplateStrings`] is [`Counter`]. - /// - /// [`Counter`]: TemplateStrings::Counter - #[must_use] - pub fn is_counter(&self) -> bool { - matches!(self, Self::Counter) - } - - /// Returns `true` if [`TemplateStrings`] is [`Extension`]. - /// - /// [`Extension`]: TemplateStrings::Extension - #[must_use] - pub fn is_extension(&self) -> bool { - matches!(self, Self::Extension) - } -} /// Mode how should be written to a file #[cfg_attr(feature = "cli", derive(ValueEnum))] diff --git a/crates/organize-rs_core/src/actions/impl_.rs b/crates/organize-rs_core/src/actions/impl_.rs index 66a66d5..4bc7427 100644 --- a/crates/organize-rs_core/src/actions/impl_.rs +++ b/crates/organize-rs_core/src/actions/impl_.rs @@ -58,8 +58,9 @@ impl ActionKind { Box::new(|entry, _preview| { Ok(ActionResultKind::Preview { msg: format!( - "{} No action: '{}'", + "{} {}: '{}'", style("(Preview)").green(), + style("No action").blue(), entry.path().display() ), path: entry.path(), @@ -73,8 +74,9 @@ impl ActionKind { if preview { Ok(ActionResultKind::Preview { msg: format!( - "{} Trash: '{}'", + "{} {}: '{}'", style("(Preview)").green(), + style("Trash").blue(), entry.path().display() ), path: entry.path(), @@ -93,8 +95,9 @@ impl ActionKind { if preview { Ok(ActionResultKind::Preview { msg: format!( - "{} Delete: '{}'", + "{} {}: '{}'", style("(Preview)").green(), + style("Delete").red(), entry.path().display() ), path: entry.path(), @@ -113,8 +116,9 @@ impl ActionKind { if preview { Ok(ActionResultKind::Preview { msg: format!( - "{} Symlink: {} -> '{}'", + "{} {}: {} -> '{}'", style("(Preview)").green(), + style("Symlink").blue(), dst.display(), entry.path().display() ), diff --git a/crates/organize-rs_core/src/filters.rs b/crates/organize-rs_core/src/filters.rs index e6ad64e..1a985e9 100644 --- a/crates/organize-rs_core/src/filters.rs +++ b/crates/organize-rs_core/src/filters.rs @@ -217,7 +217,7 @@ pub enum FilterKind { /// actions: /// - mode: preview /// action: !echo - /// msg: "Date added: {date_added.strftime('%Y-%m-%d')}" + /// msg: "Date added: {{date_added.strftime('%Y-%m-%d')}}" /// tags: /// - !custom Test::Filter::DateAdded /// # "#; diff --git a/crates/organize-rs_core/src/lib.rs b/crates/organize-rs_core/src/lib.rs index 3714dfe..97a2f1c 100644 --- a/crates/organize-rs_core/src/lib.rs +++ b/crates/organize-rs_core/src/lib.rs @@ -14,3 +14,4 @@ pub mod runner; pub mod ser_de; pub mod state; pub mod tags; +pub mod templating; diff --git a/crates/organize-rs_core/src/templating.rs b/crates/organize-rs_core/src/templating.rs new file mode 100644 index 0000000..b4ef072 --- /dev/null +++ b/crates/organize-rs_core/src/templating.rs @@ -0,0 +1,104 @@ +use displaydoc::Display; +// use filetime::FileTime; +use serde::{Deserialize, Serialize}; + +#[cfg(feature = "cli")] +use clap::ValueEnum; + +// use winnow::{ +// ascii::alpha0, +// combinator::alt, +// error::{Error, ErrorKind}, +// token::take_while, +// IResult, Parser, +// }; + +// // Display +// #[derive(Debug, Clone)] +// pub enum TemplateActionKind { +// UpperCase(String), +// LowerCase(String), +// CamelCase(String), +// SnakeCase(String), +// KebabCase(String), +// Counter(usize), +// Date(FileTime), +// } + +/// Support template strings +#[cfg_attr(feature = "cli", derive(ValueEnum))] +#[derive(Debug, Clone, Copy, Deserialize, Serialize, Display)] +pub enum TemplateStringKind { + /// {{filename}} + Filename, + /// {{counter}} + Counter, + /// {{extension}} + Extension, + /// {{date}} + Date, +} + +impl TemplateStringKind { + /// Returns `true` if [`TemplateStrings`] is [`Filename`]. + /// + /// [`Filename`]: TemplateStrings::Filename + #[must_use] + pub fn is_filename(&self) -> bool { + matches!(self, Self::Filename) + } + + /// Returns `true` if [`TemplateStrings`] is [`Counter`]. + /// + /// [`Counter`]: TemplateStrings::Counter + #[must_use] + pub fn is_counter(&self) -> bool { + matches!(self, Self::Counter) + } + + /// Returns `true` if [`TemplateStrings`] is [`Extension`]. + /// + /// [`Extension`]: TemplateStrings::Extension + #[must_use] + pub fn is_extension(&self) -> bool { + matches!(self, Self::Extension) + } +} + +// // pub fn parse_left_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { +// // (parse_digits, parse_units, parse_boundary).parse_next(input) +// // } + +// pub fn parse_units(input: &str) -> IResult<&str, &str> { +// alpha0(input) +// } +// #[cfg(test)] +// mod tests { +// use std::path::PathBuf; + +// #[test] +// fn test_unpacking_multiple_template_strings_passes() { +// let extension = "toml"; +// let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(extension)}}\"#); +// let parts = example.components(); +// parts.into_iter().map(|component| { +// component.as_os_str().to_str().map(|string| { +// let left = string.find("{{"); +// let right = string.rfind("}}"); + +// let (Some(left), Some(right)) = (left, right) else { +// return string +// }; + +// // we do `left+2..right` here, because we actually extract +// // the outer most template directly +// let template = &string[left + 2..right]; + +// assert_eq!(template, "uppercase(extension)"); + +// let left = template.find("{{"); +// let right = template.rfind("}}"); +// }) +// }) +// } +// } diff --git a/src/commands/generate/gen_config.rs b/src/commands/generate/gen_config.rs index 5de3e15..38654ad 100644 --- a/src/commands/generate/gen_config.rs +++ b/src/commands/generate/gen_config.rs @@ -16,9 +16,9 @@ use crate::application::ORGANIZE_APP; #[derive(Command, Debug, Parser, Clone)] pub struct GenConfigCmd { - /// path to an existing or to be created config file + /// path where the config file should be created at #[clap(short, long)] - path: PathBuf, + output_path: PathBuf, #[clap(flatten)] config_opts: GenConfigOpts, @@ -71,14 +71,14 @@ impl GenConfigCmd { config.add_rules(rules); - if File::open(&self.path).is_ok() { + if File::open(&self.output_path).is_ok() { if Confirm::new().with_prompt("Config file already exists. We will overwrite it, do you have a backup and want to continue?").default(false).interact()? { - config.write_to_file(&self.path, true)?; + config.write_to_file(&self.output_path, true)?; } else { bail!("Config file already exists. We will overwrite it, make sure you have a backup and agree in the dialog."); } } else { - config.write_to_file(&self.path, true)?; + config.write_to_file(&self.output_path, true)?; }; Ok(()) @@ -86,7 +86,7 @@ impl GenConfigCmd { fn generate_config_template_yaml(&self) -> Result<()> { // TODO: Handle in a better way - let path = format!("{}{}", self.path.as_path().display(), ".tmpl.yaml"); + let path = format!("{}{}", self.output_path.as_path().display(), ".tmpl.yaml"); if File::open(&path).is_ok() { if Confirm::new().with_prompt("Config file already exists. We will overwrite it, do you have a backup and want to continue?").default(false).interact()? { diff --git a/src/commands/run/config.rs b/src/commands/run/config.rs index c87ece6..97bb8a5 100644 --- a/src/commands/run/config.rs +++ b/src/commands/run/config.rs @@ -27,11 +27,14 @@ impl RunConfigCmd { fn inner_run(&self) -> Result<()> { let runner = Runner::::load_configs(&self.paths) .apply_filters(self.tags.clone()) - // .inspect_entries() + .inspect_entries() .finish_inspection() .preview_actions()?; - if Confirm::new().with_prompt("Are you sure, that you want to execute the previewed actions? This is irreversible.").default(false).interact()? { + if Confirm::new() + .with_prompt("Are you sure, that you want to execute the previewed actions? This is irreversible.") + .default(false) + .interact()? { runner.apply_actions()?; // ? Conflict handling diff --git a/src/commands/run/script.rs b/src/commands/run/script.rs index 1217d23..3e806c9 100644 --- a/src/commands/run/script.rs +++ b/src/commands/run/script.rs @@ -21,6 +21,7 @@ pub struct RunScriptCmd { impl Runnable for RunScriptCmd { fn run(&self) { + todo!("Scripting is not implemented (yet)"); match start_scripting_engine(&self.path) { Ok(_) => {} Err(err) => { diff --git a/src/lib.rs b/src/lib.rs index e684dce..aa0fcb6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -35,7 +35,6 @@ pub mod prelude; pub mod scripting; // re-exports for documentation purposes -pub use organize_rs_core::actions::{ - conflicts::ConflictResolutionKind, ActionKind, TemplateStringKind, -}; +pub use organize_rs_core::actions::{conflicts::ConflictResolutionKind, ActionKind}; pub use organize_rs_core::filters::FilterKind; +pub use organize_rs_core::templating::TemplateStringKind; From 70d198e61ed8401c64693267f5e2cd3a4f694938 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Thu, 8 Jun 2023 13:00:45 +0200 Subject: [PATCH 04/12] refactor(parsers): move size and range parsers to their own submodules --- crates/organize-rs_core/Cargo.toml | 18 - .../examples/period_parser.rs | 59 ---- .../organize-rs_core/examples/size_parser.rs | 52 --- .../examples/templating_parser.rs | 8 +- crates/organize-rs_core/src/filters.rs | 2 +- crates/organize-rs_core/src/filters/impl_.rs | 2 +- crates/organize-rs_core/src/filters/tests.rs | 2 +- crates/organize-rs_core/src/parsers.rs | 310 +----------------- .../src/parsers/period_range.rs | 174 ++++++++++ .../src/parsers/size_range.rs | 181 ++++++++++ 10 files changed, 368 insertions(+), 440 deletions(-) delete mode 100644 crates/organize-rs_core/examples/period_parser.rs delete mode 100644 crates/organize-rs_core/examples/size_parser.rs create mode 100644 crates/organize-rs_core/src/parsers/period_range.rs create mode 100644 crates/organize-rs_core/src/parsers/size_range.rs diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index af71a1e..aff71e5 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -11,24 +11,6 @@ description = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html include = ["src/**/*", "LICENSE", "CHANGELOG.md", "config/config_template.yaml"] -[[example]] -name = "size_parser" -path = "examples/size_parser.rs" -test = true -bench = true -doc = true -harness = true -edition = "2021" - -[[example]] -name = "period_parser" -path = "examples/period_parser.rs" -test = true -bench = true -doc = true -harness = true -edition = "2021" - [[example]] name = "templating_parser" path = "examples/templating_parser.rs" diff --git a/crates/organize-rs_core/examples/period_parser.rs b/crates/organize-rs_core/examples/period_parser.rs deleted file mode 100644 index d07bf50..0000000 --- a/crates/organize-rs_core/examples/period_parser.rs +++ /dev/null @@ -1,59 +0,0 @@ -use winnow::{ascii::alpha0, combinator::alt, token::take_while, IResult, Parser}; - -pub fn parse_garbage(input: &str) -> IResult<&str, &str> { - take_while(1.., " ,").parse_next(input) -} - -pub fn parse_eq_operators(input: &str) -> IResult<&str, &str> { - take_while(1.., "><=").parse_next(input) -} - -pub fn parse_boundary(input: &str) -> IResult<&str, &str> { - alt((("..="), (".."))).parse_next(input) -} - -pub fn parse_digits(input: &str) -> IResult<&str, f64> { - take_while(1.., (('0'..='9'), ('.'))) - .parse_to() - .parse_next(input) -} - -pub fn parse_right_boundary(input: &str) -> IResult<&str, (&str, f64, &str)> { - (parse_boundary, parse_digits, parse_units).parse_next(input) -} - -pub fn parse_left_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { - (parse_digits, parse_units, parse_boundary).parse_next(input) -} - -pub fn parse_whole_condition(input: &str) -> IResult<&str, (f64, &str, &str, f64, &str)> { - ( - parse_digits, - parse_units, - parse_boundary, - parse_digits, - parse_units, - ) - .parse_next(input) -} - -pub fn parse_whole_period(input: &str) -> IResult<&str, (f64, &str, f64, &str)> { - (parse_digits, parse_boundary, parse_digits, parse_units).parse_next(input) -} - -pub fn parse_units(input: &str) -> IResult<&str, &str> { - alpha0(input) -} - -fn main() { - let input = "1d..7d"; - let _input2 = "7d.."; - let _input3 = "..7d"; - - let (remainder, result) = (parse_whole_condition).parse_next(input).unwrap(); - // let (_, _result) = (parse_left_boundary).parse_next(input2).unwrap(); - // let (_, _result) = (parse_right_boundary).parse_next(input3).unwrap(); - - assert_eq!(remainder, ""); - assert_eq!(result, (1f64, "d", "..", 7f64, "d")) -} diff --git a/crates/organize-rs_core/examples/size_parser.rs b/crates/organize-rs_core/examples/size_parser.rs deleted file mode 100644 index e50b3d3..0000000 --- a/crates/organize-rs_core/examples/size_parser.rs +++ /dev/null @@ -1,52 +0,0 @@ -use winnow::{ascii::alpha0, combinator::alt, token::take_while, IResult, Parser}; - -pub fn parse_garbage(input: &str) -> IResult<&str, &str> { - take_while(1.., " ,").parse_next(input) -} - -pub fn parse_eq_operators(input: &str) -> IResult<&str, &str> { - take_while(1.., "><=").parse_next(input) -} - -pub fn parse_boundary(input: &str) -> IResult<&str, &str> { - alt((("..="), (".."))).parse_next(input) -} - -pub fn parse_digits(input: &str) -> IResult<&str, f64> { - take_while(1.., (('0'..='9'), ('.'))) - .parse_to() - .parse_next(input) -} - -pub fn parse_right_boundary(input: &str) -> IResult<&str, (&str, f64, &str)> { - (parse_boundary, parse_digits, parse_units).parse_next(input) -} - -pub fn parse_left_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { - (parse_digits, parse_units, parse_boundary).parse_next(input) -} - -pub fn parse_whole_condition(input: &str) -> IResult<&str, (f64, &str, &str, f64, &str)> { - ( - parse_digits, - parse_units, - parse_boundary, - parse_digits, - parse_units, - ) - .parse_next(input) -} - -pub fn parse_units(input: &str) -> IResult<&str, &str> { - alpha0(input) -} - -fn main() { - let input = "1.5MiB..100.3MB"; - let input2 = "5GB.."; - let input3 = "..0.5GiB"; - - let (_, _result) = (parse_whole_condition).parse_next(input).unwrap(); - let (_, _result) = (parse_left_boundary).parse_next(input2).unwrap(); - let (_, _result) = (parse_right_boundary).parse_next(input3).unwrap(); -} diff --git a/crates/organize-rs_core/examples/templating_parser.rs b/crates/organize-rs_core/examples/templating_parser.rs index 53f5ee6..613203a 100644 --- a/crates/organize-rs_core/examples/templating_parser.rs +++ b/crates/organize-rs_core/examples/templating_parser.rs @@ -65,10 +65,11 @@ fn main() { let case2 = "{{uppercase(extension)}}"; let case3 = "{{lowercase(entry.name)}}"; let case4 = "{{entry.created.year}}"; - let case5 = "{{file_content.customer}}"; + let case5 = "{{entry.content.customer}}"; let case6 = "{{entry.metadata.last_modified.year}}"; let case7 = "{{size.traditional}} -- {{relative_path}}"; - let case8 = "{{date_added.strftime('%Y-%m-%d')}}"; + let case8 = "{{strftime(entry.metadata.date_added, '%Y-%m-%d')}}"; // ! '%Y-%m-%d' should be parsed as a single value + // ! see https://docs.rs/chrono/0.4.26/chrono/#formatting-and-parsing let (remainder, result) = (parse_single_template).parse_next(case1).unwrap(); assert_eq!(result, ("extension")); @@ -79,9 +80,10 @@ fn main() { let (remainder, result) = (parse_dotted_template).parse_next(case4).unwrap(); assert_eq!(result, (vec!["entry", "created", "year"])); let (remainder, result) = (parse_dotted_template).parse_next(case5).unwrap(); - assert_eq!(result, (vec!["file_content", "customer"])); + assert_eq!(result, (vec!["entry", "content", "customer"])); let (remainder, result) = (parse_dotted_template).parse_next(case6).unwrap(); assert_eq!(result, (vec!["entry", "metadata", "last_modified", "year"])); + let (remainder, result) = (parse_dotted_template).parse_next(case7).unwrap(); println!("{remainder} - {result:?}"); } diff --git a/crates/organize-rs_core/src/filters.rs b/crates/organize-rs_core/src/filters.rs index 1a985e9..c5a6c9b 100644 --- a/crates/organize-rs_core/src/filters.rs +++ b/crates/organize-rs_core/src/filters.rs @@ -18,7 +18,7 @@ use serde_with::StringWithSeparator; use serde_with::serde_as; -use crate::parsers::{PeriodRange, SizeRange}; +use crate::parsers::{period_range::PeriodRange, size_range::SizeRange}; pub type FilterClosure<'a, C> = Box) -> bool + 'a>; pub type FilterClosureCollection<'a, C> = Vec>; diff --git a/crates/organize-rs_core/src/filters/impl_.rs b/crates/organize-rs_core/src/filters/impl_.rs index 69939b8..c37bcf8 100644 --- a/crates/organize-rs_core/src/filters/impl_.rs +++ b/crates/organize-rs_core/src/filters/impl_.rs @@ -11,7 +11,7 @@ use crate::{ FilterCollection, FilterGroup, FilterGroupOperationKind, FilterKind, FilterOperationKind, NameFilterArgs, RecursiveFilterArgs, }, - parsers::{PeriodRange, SizeRange}, + parsers::{period_range::PeriodRange, size_range::SizeRange}, }; impl FilterKind { diff --git a/crates/organize-rs_core/src/filters/tests.rs b/crates/organize-rs_core/src/filters/tests.rs index 1a03f23..929397b 100644 --- a/crates/organize-rs_core/src/filters/tests.rs +++ b/crates/organize-rs_core/src/filters/tests.rs @@ -19,7 +19,7 @@ use filetime::{self, FileTime}; use crate::{ filters::{FilterKind, NameFilterArgs}, - parsers::{PeriodRange, SizeRange}, + parsers::{period_range::PeriodRange, size_range::SizeRange}, }; fn get_fixtures_dir() -> PathBuf { diff --git a/crates/organize-rs_core/src/parsers.rs b/crates/organize-rs_core/src/parsers.rs index 022e358..439d5c2 100644 --- a/crates/organize-rs_core/src/parsers.rs +++ b/crates/organize-rs_core/src/parsers.rs @@ -1,8 +1,11 @@ -//! parser +//! parsers + +pub mod period_range; +pub mod size_range; use std::{fmt::Display, ops::Range, str::FromStr}; -use serde::{Serialize}; +use serde::Serialize; use serde_with::DeserializeFromStr; use winnow::{ ascii::alpha0, @@ -57,41 +60,6 @@ pub fn parse_units(input: &str) -> IResult<&str, &str> { alpha0(input) } -#[derive(Debug, Clone, PartialEq, DeserializeFromStr, Serialize)] -pub struct PeriodRange(Range); - -impl PeriodRange { - pub const MIN: f64 = 0f64; - pub const MAX: f64 = 9.467e+8f64; // 30 years in seconds - - pub fn in_range(&self, value: f64) -> bool { - self.0.contains(&value) - } -} - -impl Display for PeriodRange { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "PeriodRange({:?})", self.0) - } -} - -#[derive(Debug, Clone, PartialEq, DeserializeFromStr, Serialize)] -pub struct SizeRange(Range); - -impl Display for SizeRange { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "SizeRange({:?})", self.0) - } -} -impl SizeRange { - pub const MIN: f64 = 1f64; // 1 byte - pub const MAX: f64 = 4e+12f64; // 4 TB in bytes - - pub fn in_range(&self, size: f64) -> bool { - self.0.contains(&size) - } -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum BoundarySide { Left, @@ -115,271 +83,3 @@ impl Condition { } } } - -impl FromStr for PeriodRange { - type Err = Error; - - fn from_str(input: &str) -> Result { - let mut condition = Condition(None, None); - - match ( - parse_left_boundary(input), - parse_right_boundary(input), - parse_whole_condition(input), - ) { - (Ok(left_boundary), Err(_), Err(_)) => { - let (_, (value, unit, _)) = left_boundary; - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value, unit)).into_seconds(), - side: BoundarySide::Left, - }); - } - (Err(_), Ok(right_boundary), Err(_)) => { - let (_, (_, value, unit)) = right_boundary; - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value, unit)).into_seconds(), - side: BoundarySide::Right, - }); - } - (_, _, Ok(whole_condition)) => { - let (_, (value_left, unit_left, _, value_right, unit_right)) = whole_condition; - - if unit_left != unit_right { - return Err(Error { - input: format!("Left unit {unit_left} and right unit {unit_right} need to be equal: {input}"), - kind: ErrorKind::Verify, - }); - } else if value_left >= value_right { - return Err(Error { - input: format!("Left value {value_left} can't be greater or equal than the right value {value_right}: {input}"), - kind: ErrorKind::Verify, - }); - } - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value_left, unit_left)).into_seconds(), - side: BoundarySide::Left, - }); - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value_right, unit_right)).into_seconds(), - side: BoundarySide::Right, - }); - } - _ => { - return Err(Error { - input: format!("Size condition pattern not recognized for: {input}"), - kind: ErrorKind::Verify, - }) - } - } - - match condition { - Condition(Some(left), None) => Ok(Self(Range { - start: left.value, - end: PeriodRange::MAX, - })), - Condition(None, Some(right)) => Ok(Self(Range { - start: PeriodRange::MIN, - end: right.value, - })), - Condition(Some(left), Some(right)) if left.value < right.value => Ok(Self(Range { - start: left.value, - end: right.value, - })), - _ => unreachable!("shouldn't be able to create a condition!"), - } - } -} - -impl FromStr for SizeRange { - type Err = Error; - - fn from_str(input: &str) -> Result { - let mut condition = Condition(None, None); - - match ( - parse_left_boundary(input), - parse_right_boundary(input), - parse_whole_condition(input), - ) { - (Ok(left_boundary), Err(_), Err(_)) => { - let (_, (size, unit, _)) = left_boundary; - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { - input: format!("Couldn't convert {size} {unit} to bytes: {err}"), - kind: ErrorKind::Fail, - })?, - side: BoundarySide::Left, - }); - } - (Err(_), Ok(right_boundary), Err(_)) => { - let (_, (_, size, unit)) = right_boundary; - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { - input: format!("Couldn't convert {size} {unit} to bytes: {err}"), - kind: ErrorKind::Fail, - })?, - side: BoundarySide::Right, - }); - } - (_, _, Ok(whole_condition)) => { - let (_, (size_left, unit_left, _, size_right, unit_right)) = whole_condition; - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size_left} {unit_left}")).map_err(|err| { - Error { - input: format!( - "Couldn't convert {size_left} {unit_left} to bytes: {err}" - ), - kind: ErrorKind::Fail, - } - })?, - side: BoundarySide::Left, - }); - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size_right} {unit_right}")).map_err(|err| { - Error { - input: format!( - "Couldn't convert {size_right} {unit_right} to bytes: {err}" - ), - kind: ErrorKind::Fail, - } - })?, - side: BoundarySide::Right, - }); - } - _ => { - return Err(Error { - input: format!("Size condition pattern not recognized for: {input}"), - kind: ErrorKind::Verify, - }) - } - } - - match condition { - Condition(Some(left), None) => Ok(Self(Range { - start: left.value.get_bytes() as f64, - end: SizeRange::MAX, - })), - Condition(None, Some(right)) => Ok(Self(Range { - start: SizeRange::MIN, - end: right.value.get_bytes() as f64, - })), - Condition(Some(left), Some(right)) - if left.value.get_bytes() < right.value.get_bytes() => - { - Ok(Self(Range { - start: left.value.get_bytes() as f64, - end: right.value.get_bytes() as f64, - })) - } - Condition(Some(left), Some(right)) - if left.value.get_bytes() >= right.value.get_bytes() => - { - Err(Error { - input: format!( - "Left value {} can't be greater or equal than the right value {}: {input}", - left.value.get_bytes(), - right.value.get_bytes() - ), - kind: ErrorKind::Verify, - }) - } - _ => unreachable!("shouldn't be able to create a condition!"), - } - } -} - -#[cfg(test)] -mod tests { - - use super::*; - - #[test] - fn parse_left_size_condition_to_range_passes() { - let condition = "5.0GiB.."; - let range = SizeRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - SizeRange( - 5368709120.0..4000000000000.0, - ) - "###); - } - - #[test] - fn parse_right_size_condition_to_range_passes() { - let condition = "..0.5GiB"; - let range = SizeRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - SizeRange( - 1.0..536870912.0, - ) - "###); - } - - #[test] - fn parse_whole_size_condition_to_range_passes() { - let condition = "1.5MiB..100.3MB"; - let range = SizeRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - SizeRange( - 1572864.0..100300000.0, - ) - "###); - } - - #[test] - fn parse_whole_period_condition_to_range_passes() { - let condition = "1d..7d"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - PeriodRange( - 86400.0..604800.0, - ) - "###); - } - - #[test] - fn parse_left_period_condition_to_range_passes() { - let condition = "1d.."; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - PeriodRange( - 86400.0..946700000.0, - ) - "###); - } - - #[test] - fn parse_right_period_condition_to_range_passes() { - let condition = "..1d"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r###" - PeriodRange( - 0.0..86400.0, - ) - "###); - } - - #[test] - #[should_panic] - fn parse_different_units_on_whole_period_condition_to_range_fails() { - let condition = "1w..7d"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r""); - } - - #[test] - #[should_panic] - fn parse_non_standard_units_on_whole_period_condition_to_range_fails() { - let condition = "1f..7f"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r""); - } -} diff --git a/crates/organize-rs_core/src/parsers/period_range.rs b/crates/organize-rs_core/src/parsers/period_range.rs new file mode 100644 index 0000000..4640857 --- /dev/null +++ b/crates/organize-rs_core/src/parsers/period_range.rs @@ -0,0 +1,174 @@ +//! parser for [`PeriodRange`] + +use std::{fmt::Display, ops::Range, str::FromStr}; + +use serde::Serialize; +use serde_with::DeserializeFromStr; +use winnow::{ + ascii::alpha0, + combinator::alt, + error::{Error, ErrorKind}, + token::take_while, + IResult, Parser, +}; + +use byte_unit::Byte; + +use crate::{ + filters::DateUnitKind, + parsers::{ + parse_left_boundary, parse_right_boundary, parse_whole_condition, BoundarySide, Condition, + SingleCondition, + }, +}; + +#[derive(Debug, Clone, PartialEq, DeserializeFromStr, Serialize)] +pub struct PeriodRange(Range); + +impl PeriodRange { + pub const MIN: f64 = 0f64; + pub const MAX: f64 = 9.467e+8f64; // 30 years in seconds + + pub fn in_range(&self, value: f64) -> bool { + self.0.contains(&value) + } +} + +impl Display for PeriodRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "PeriodRange({:?})", self.0) + } +} + +impl FromStr for PeriodRange { + type Err = Error; + + fn from_str(input: &str) -> Result { + let mut condition = Condition(None, None); + + match ( + parse_left_boundary(input), + parse_right_boundary(input), + parse_whole_condition(input), + ) { + (Ok(left_boundary), Err(_), Err(_)) => { + let (_, (value, unit, _)) = left_boundary; + + condition.set_condition(SingleCondition { + value: DateUnitKind::from((value, unit)).into_seconds(), + side: BoundarySide::Left, + }); + } + (Err(_), Ok(right_boundary), Err(_)) => { + let (_, (_, value, unit)) = right_boundary; + + condition.set_condition(SingleCondition { + value: DateUnitKind::from((value, unit)).into_seconds(), + side: BoundarySide::Right, + }); + } + (_, _, Ok(whole_condition)) => { + let (_, (value_left, unit_left, _, value_right, unit_right)) = whole_condition; + + if unit_left != unit_right { + return Err(Error { + input: format!("Left unit {unit_left} and right unit {unit_right} need to be equal: {input}"), + kind: ErrorKind::Verify, + }); + } else if value_left >= value_right { + return Err(Error { + input: format!("Left value {value_left} can't be greater or equal than the right value {value_right}: {input}"), + kind: ErrorKind::Verify, + }); + } + + condition.set_condition(SingleCondition { + value: DateUnitKind::from((value_left, unit_left)).into_seconds(), + side: BoundarySide::Left, + }); + + condition.set_condition(SingleCondition { + value: DateUnitKind::from((value_right, unit_right)).into_seconds(), + side: BoundarySide::Right, + }); + } + _ => { + return Err(Error { + input: format!("Size condition pattern not recognized for: {input}"), + kind: ErrorKind::Verify, + }) + } + } + + match condition { + Condition(Some(left), None) => Ok(Self(Range { + start: left.value, + end: PeriodRange::MAX, + })), + Condition(None, Some(right)) => Ok(Self(Range { + start: PeriodRange::MIN, + end: right.value, + })), + Condition(Some(left), Some(right)) if left.value < right.value => Ok(Self(Range { + start: left.value, + end: right.value, + })), + _ => unreachable!("shouldn't be able to create a condition!"), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn parse_whole_period_condition_to_range_passes() { + let condition = "1d..7d"; + let range = PeriodRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + PeriodRange( + 86400.0..604800.0, + ) + "###); + } + + #[test] + fn parse_left_period_condition_to_range_passes() { + let condition = "1d.."; + let range = PeriodRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + PeriodRange( + 86400.0..946700000.0, + ) + "###); + } + + #[test] + fn parse_right_period_condition_to_range_passes() { + let condition = "..1d"; + let range = PeriodRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + PeriodRange( + 0.0..86400.0, + ) + "###); + } + + #[test] + #[should_panic] + fn parse_different_units_on_whole_period_condition_to_range_fails() { + let condition = "1w..7d"; + let range = PeriodRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r""); + } + + #[test] + #[should_panic] + fn parse_non_standard_units_on_whole_period_condition_to_range_fails() { + let condition = "1f..7f"; + let range = PeriodRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r""); + } +} diff --git a/crates/organize-rs_core/src/parsers/size_range.rs b/crates/organize-rs_core/src/parsers/size_range.rs new file mode 100644 index 0000000..4accd3c --- /dev/null +++ b/crates/organize-rs_core/src/parsers/size_range.rs @@ -0,0 +1,181 @@ +//! parser for [`SizeRange`] + +use std::{fmt::Display, ops::Range, str::FromStr}; + +use serde::Serialize; +use serde_with::DeserializeFromStr; +use winnow::{ + ascii::alpha0, + combinator::alt, + error::{Error, ErrorKind}, + token::take_while, + IResult, Parser, +}; + +use byte_unit::Byte; + +use crate::{ + filters::DateUnitKind, + parsers::{ + parse_left_boundary, parse_right_boundary, parse_whole_condition, BoundarySide, Condition, + SingleCondition, + }, +}; + +#[derive(Debug, Clone, PartialEq, DeserializeFromStr, Serialize)] +pub struct SizeRange(Range); + +impl Display for SizeRange { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "SizeRange({:?})", self.0) + } +} +impl SizeRange { + pub const MIN: f64 = 1f64; // 1 byte + pub const MAX: f64 = 4e+12f64; // 4 TB in bytes + + pub fn in_range(&self, size: f64) -> bool { + self.0.contains(&size) + } +} + +impl FromStr for SizeRange { + type Err = Error; + + fn from_str(input: &str) -> Result { + let mut condition = Condition(None, None); + + match ( + parse_left_boundary(input), + parse_right_boundary(input), + parse_whole_condition(input), + ) { + (Ok(left_boundary), Err(_), Err(_)) => { + let (_, (size, unit, _)) = left_boundary; + + condition.set_condition(SingleCondition { + value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { + input: format!("Couldn't convert {size} {unit} to bytes: {err}"), + kind: ErrorKind::Fail, + })?, + side: BoundarySide::Left, + }); + } + (Err(_), Ok(right_boundary), Err(_)) => { + let (_, (_, size, unit)) = right_boundary; + + condition.set_condition(SingleCondition { + value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { + input: format!("Couldn't convert {size} {unit} to bytes: {err}"), + kind: ErrorKind::Fail, + })?, + side: BoundarySide::Right, + }); + } + (_, _, Ok(whole_condition)) => { + let (_, (size_left, unit_left, _, size_right, unit_right)) = whole_condition; + + condition.set_condition(SingleCondition { + value: Byte::from_str(format!("{size_left} {unit_left}")).map_err(|err| { + Error { + input: format!( + "Couldn't convert {size_left} {unit_left} to bytes: {err}" + ), + kind: ErrorKind::Fail, + } + })?, + side: BoundarySide::Left, + }); + + condition.set_condition(SingleCondition { + value: Byte::from_str(format!("{size_right} {unit_right}")).map_err(|err| { + Error { + input: format!( + "Couldn't convert {size_right} {unit_right} to bytes: {err}" + ), + kind: ErrorKind::Fail, + } + })?, + side: BoundarySide::Right, + }); + } + _ => { + return Err(Error { + input: format!("Size condition pattern not recognized for: {input}"), + kind: ErrorKind::Verify, + }) + } + } + + match condition { + Condition(Some(left), None) => Ok(Self(Range { + start: left.value.get_bytes() as f64, + end: SizeRange::MAX, + })), + Condition(None, Some(right)) => Ok(Self(Range { + start: SizeRange::MIN, + end: right.value.get_bytes() as f64, + })), + Condition(Some(left), Some(right)) + if left.value.get_bytes() < right.value.get_bytes() => + { + Ok(Self(Range { + start: left.value.get_bytes() as f64, + end: right.value.get_bytes() as f64, + })) + } + Condition(Some(left), Some(right)) + if left.value.get_bytes() >= right.value.get_bytes() => + { + Err(Error { + input: format!( + "Left value {} can't be greater or equal than the right value {}: {input}", + left.value.get_bytes(), + right.value.get_bytes() + ), + kind: ErrorKind::Verify, + }) + } + _ => unreachable!("shouldn't be able to create a condition!"), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn parse_left_size_condition_to_range_passes() { + let condition = "5.0GiB.."; + let range = SizeRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + SizeRange( + 5368709120.0..4000000000000.0, + ) + "###); + } + + #[test] + fn parse_right_size_condition_to_range_passes() { + let condition = "..0.5GiB"; + let range = SizeRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + SizeRange( + 1.0..536870912.0, + ) + "###); + } + + #[test] + fn parse_whole_size_condition_to_range_passes() { + let condition = "1.5MiB..100.3MB"; + let range = SizeRange::from_str(condition).unwrap(); + insta::assert_debug_snapshot!(range, @r###" + SizeRange( + 1572864.0..100300000.0, + ) + "###); + } +} From 3388b5c0c3d54b402a2433af4a2b9bacad9ac02c Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Thu, 8 Jun 2023 23:13:18 +0200 Subject: [PATCH 05/12] feat(templating): add more unit tests for templating --- TODO.md | 3 +- crates/organize-rs_core/Cargo.toml | 16 +- .../examples/templating_parser.rs | 89 ---- crates/organize-rs_core/src/actions.rs | 8 +- crates/organize-rs_core/src/parsers.rs | 110 +++-- .../src/parsers/period_range.rs | 76 ++-- .../src/parsers/size_range.rs | 77 ++-- .../organize-rs_core/src/parsers/template.rs | 147 +++++++ crates/organize-rs_core/src/templating.rs | 386 +++++++++++++----- src/lib.rs | 2 +- 10 files changed, 594 insertions(+), 320 deletions(-) delete mode 100644 crates/organize-rs_core/examples/templating_parser.rs create mode 100644 crates/organize-rs_core/src/parsers/template.rs diff --git a/TODO.md b/TODO.md index f0a71e8..2654af7 100644 --- a/TODO.md +++ b/TODO.md @@ -46,8 +46,7 @@ - .ini 1. implement file name templating - - or bare with `.replace` and own placeholders like `{{file_name}}`, `{{counter}}` etc. - - support batch renaming (e.g. rename all images in a directory to image_n where n is a number +1. support batch renaming (e.g. rename all images in a directory to image_n where n is a number - remove some prefix from filenames - we want to have filename tags as well e.g. `dnt_` (do not touch) or `wip_` so these things are treated differently - also we want to have `{project_name}_` that we can remove after moving to a project folder diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index aff71e5..29cd332 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -11,14 +11,14 @@ description = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html include = ["src/**/*", "LICENSE", "CHANGELOG.md", "config/config_template.yaml"] -[[example]] -name = "templating_parser" -path = "examples/templating_parser.rs" -test = true -bench = true -doc = true -harness = true -edition = "2021" +# [[example]] +# name = "templating_parser" +# path = "examples/templating_parser.rs" +# test = true +# bench = true +# doc = true +# harness = true +# edition = "2021" [features] default = [] diff --git a/crates/organize-rs_core/examples/templating_parser.rs b/crates/organize-rs_core/examples/templating_parser.rs deleted file mode 100644 index 613203a..0000000 --- a/crates/organize-rs_core/examples/templating_parser.rs +++ /dev/null @@ -1,89 +0,0 @@ -use winnow::{ - ascii::alpha0, - combinator::alt, - error::{Error, ErrorKind}, - multi::separated0, - sequence::{delimited, preceded}, - token::take_while, - IResult, Parser, -}; -use winnow::{ - stream::AsChar, - token::{any, tag}, -}; - -pub fn parse_text_incl_underscore_hyphen(input: &str) -> IResult<&str, &str> { - take_while(1.., (AsChar::is_alpha, ('_'), ('-'))).parse_next(input) -} - -pub fn parse_boundary(input: &str) -> IResult<&str, &str> { - take_while(1.., "{()}").parse_next(input) -} - -pub fn parse_single_template(input: &str) -> IResult<&str, &str> { - let (remainder, (_, text, _)) = - (parse_boundary, parse_template_content, parse_boundary).parse_next(input)?; - Ok((remainder, text)) -} - -pub fn parse_transform_template(input: &str) -> IResult<&str, (&str, Vec<&str>)> { - let (_, template) = parse_single_template.parse_next(input)?; - - let (remainder, (text, _, arg, _)) = ( - parse_text_incl_underscore_hyphen, - parse_boundary, - parse_separated_text, - parse_boundary, - ) - .parse_next(template)?; - Ok((remainder, (text, arg))) -} - -pub fn parse_dotted_template(input: &str) -> IResult<&str, Vec<&str>> { - let (_, template) = parse_single_template.parse_next(input)?; - - (parse_separated_text).parse_next(template) -} - -fn parse_separated_text(input: &str) -> IResult<&str, Vec<&str>> { - // take_while(1.., (AsChar::is_alpha, ('.'))).parse_next(input) - separated0(parse_text_incl_underscore_hyphen, '.').parse_next(input) -} - -fn parse_template_content(input: &str) -> IResult<&str, &str> { - take_while(1.., (AsChar::is_alpha, ('.'), ('_'), ('('), (')'))).parse_next(input) -} - -// pub fn parse_transform_template_with_sub_key(input: &str) -> IResult<&str, (&str, Vec<&str>)> { -// let (remainder, (function, arg)) = parse_transform_template.parse_next(input)?; -// let (_, args) = parse_arg_dot.parse_next(arg)?; -// Ok((remainder, (function, args))) -// } - -fn main() { - let case1 = "{{extension}}"; - let case2 = "{{uppercase(extension)}}"; - let case3 = "{{lowercase(entry.name)}}"; - let case4 = "{{entry.created.year}}"; - let case5 = "{{entry.content.customer}}"; - let case6 = "{{entry.metadata.last_modified.year}}"; - let case7 = "{{size.traditional}} -- {{relative_path}}"; - let case8 = "{{strftime(entry.metadata.date_added, '%Y-%m-%d')}}"; // ! '%Y-%m-%d' should be parsed as a single value - // ! see https://docs.rs/chrono/0.4.26/chrono/#formatting-and-parsing - - let (remainder, result) = (parse_single_template).parse_next(case1).unwrap(); - assert_eq!(result, ("extension")); - let (remainder, result) = (parse_transform_template).parse_next(case2).unwrap(); - assert_eq!(result, ("uppercase", vec!["extension"])); - let (remainder, result) = (parse_transform_template).parse_next(case3).unwrap(); - assert_eq!(result, ("lowercase", vec!["entry", "name"])); - let (remainder, result) = (parse_dotted_template).parse_next(case4).unwrap(); - assert_eq!(result, (vec!["entry", "created", "year"])); - let (remainder, result) = (parse_dotted_template).parse_next(case5).unwrap(); - assert_eq!(result, (vec!["entry", "content", "customer"])); - let (remainder, result) = (parse_dotted_template).parse_next(case6).unwrap(); - assert_eq!(result, (vec!["entry", "metadata", "last_modified", "year"])); - let (remainder, result) = (parse_dotted_template).parse_next(case7).unwrap(); - - println!("{remainder} - {result:?}"); -} diff --git a/crates/organize-rs_core/src/actions.rs b/crates/organize-rs_core/src/actions.rs index fd9c036..00f5e9f 100644 --- a/crates/organize-rs_core/src/actions.rs +++ b/crates/organize-rs_core/src/actions.rs @@ -18,7 +18,7 @@ use serde::{Deserialize, Serialize}; use crate::{ actions::conflicts::{ConflictKind, ConflictResolutionKind}, - error::OrganizeError, templating::TemplateStringKind, + error::OrganizeError, }; type ActionClosure<'a, C> = @@ -355,7 +355,7 @@ pub enum ActionKind { /// Defaults to `{name}_{counter}{extension}` #[cfg_attr(feature = "cli", arg(long))] #[serde(default = "Option::default")] - rename_template: Option>, + rename_template: Option>, /// An opener url of the filesystem you want to copy to. /// /// If this is not given, the local filesystem is used. @@ -577,7 +577,7 @@ pub enum ActionKind { /// Defaults to `{name}_{counter}{extension}` #[cfg_attr(feature = "cli", arg(long))] #[serde(default = "Option::default")] - rename_template: Option>, + rename_template: Option>, /// An opener url of the filesystem you want to move to. /// /// If this is not given, the local filesystem is used. @@ -636,7 +636,7 @@ pub enum ActionKind { /// Defaults to `{name}_{counter}{extension}` #[cfg_attr(feature = "cli", arg(long))] #[serde(default = "Option::default")] - rename_template: Option>, + rename_template: Option>, }, /// Execute a shell command /// diff --git a/crates/organize-rs_core/src/parsers.rs b/crates/organize-rs_core/src/parsers.rs index 439d5c2..104f903 100644 --- a/crates/organize-rs_core/src/parsers.rs +++ b/crates/organize-rs_core/src/parsers.rs @@ -2,84 +2,114 @@ pub mod period_range; pub mod size_range; +pub mod template; -use std::{fmt::Display, ops::Range, str::FromStr}; - -use serde::Serialize; -use serde_with::DeserializeFromStr; use winnow::{ - ascii::alpha0, + ascii::{alpha0, float}, combinator::alt, - error::{Error, ErrorKind}, - token::take_while, IResult, Parser, }; -use byte_unit::Byte; - -use crate::filters::DateUnitKind; - -pub fn parse_garbage(input: &str) -> IResult<&str, &str> { - take_while(1.., " ,").parse_next(input) -} - -pub fn parse_eq_operators(input: &str) -> IResult<&str, &str> { - take_while(1.., "><=").parse_next(input) -} - -pub fn parse_boundary(input: &str) -> IResult<&str, &str> { +pub fn parse_range_boundary(input: &str) -> IResult<&str, &str> { alt((("..="), (".."))).parse_next(input) } pub fn parse_digits(input: &str) -> IResult<&str, f64> { - take_while(1.., (('0'..='9'), ('.'))) - .parse_to() - .parse_next(input) + float(input) +} + +pub fn parse_right_range_boundary(input: &str) -> IResult<&str, (&str, f64, &str)> { + (parse_range_boundary, parse_digits, parse_units).parse_next(input) } -pub fn parse_right_boundary(input: &str) -> IResult<&str, (&str, f64, &str)> { - (parse_boundary, parse_digits, parse_units).parse_next(input) +pub fn parse_left_range_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { + (parse_digits, parse_units, parse_range_boundary).parse_next(input) } -pub fn parse_left_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { - (parse_digits, parse_units, parse_boundary).parse_next(input) +pub fn parse_units(input: &str) -> IResult<&str, &str> { + alpha0(input) } -pub fn parse_whole_condition(input: &str) -> IResult<&str, (f64, &str, &str, f64, &str)> { +pub fn parse_whole_range(input: &str) -> IResult<&str, (f64, &str, &str, f64, &str)> { ( parse_digits, parse_units, - parse_boundary, + parse_range_boundary, parse_digits, parse_units, ) .parse_next(input) } -pub fn parse_units(input: &str) -> IResult<&str, &str> { - alpha0(input) -} - #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum BoundarySide { +pub enum RangeBoundarySide { Left, Right, } #[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct SingleCondition { +pub struct SingleRangeCondition { value: T, - side: BoundarySide, + side: RangeBoundarySide, } #[derive(Debug, Clone, Copy)] -pub struct Condition(Option>, Option>); +pub struct Condition( + Option>, + Option>, +); impl Condition { - pub fn set_condition(&mut self, condition: SingleCondition) { + pub fn set_condition(&mut self, condition: SingleRangeCondition) { match condition.side { - BoundarySide::Left => self.0 = Some(condition), - BoundarySide::Right => self.1 = Some(condition), + RangeBoundarySide::Left => self.0 = Some(condition), + RangeBoundarySide::Right => self.1 = Some(condition), } } } + +#[cfg(test)] +mod tests { + use rstest::rstest; + + use super::*; + + #[rstest] + #[should_panic] + #[case("1d..7d", (1.0, "d", ".."))] + #[case("1w..", (1.0, "w", "..") )] + fn test_parse_left_range_boundary_passes( + #[case] condition: &str, + #[case] outcome: (f64, &str, &str), + ) { + let (remainder, result) = (parse_left_range_boundary).parse_next(condition).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } + + #[rstest] + #[should_panic] + #[case("1d..7d", ("..", 7.0, "d"))] + #[case("..1w", ("..", 1.0, "w"))] + fn test_parse_right_range_boundary_passes( + #[case] condition: &str, + #[case] outcome: (&str, f64, &str), + ) { + let (remainder, result) = (parse_right_range_boundary).parse_next(condition).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } + + #[rstest] + #[should_panic] + #[case("1w..", (1.0, "w", "..", 0.0, "") )] + #[case("1d..7d", (1.0, "d", "..", 7.0, "d"))] + fn test_parse_whole_range_passes( + #[case] condition: &str, + #[case] outcome: (f64, &str, &str, f64, &str), + ) { + let (remainder, result) = (parse_whole_range).parse_next(condition).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } +} diff --git a/crates/organize-rs_core/src/parsers/period_range.rs b/crates/organize-rs_core/src/parsers/period_range.rs index 4640857..69b5daa 100644 --- a/crates/organize-rs_core/src/parsers/period_range.rs +++ b/crates/organize-rs_core/src/parsers/period_range.rs @@ -5,20 +5,16 @@ use std::{fmt::Display, ops::Range, str::FromStr}; use serde::Serialize; use serde_with::DeserializeFromStr; use winnow::{ - ascii::alpha0, - combinator::alt, - error::{Error, ErrorKind}, - token::take_while, - IResult, Parser, + error::{Error, ErrorKind}, Parser, }; -use byte_unit::Byte; + use crate::{ filters::DateUnitKind, parsers::{ - parse_left_boundary, parse_right_boundary, parse_whole_condition, BoundarySide, Condition, - SingleCondition, + parse_left_range_boundary, parse_right_range_boundary, parse_whole_range, Condition, + RangeBoundarySide, SingleRangeCondition, }, }; @@ -47,27 +43,11 @@ impl FromStr for PeriodRange { let mut condition = Condition(None, None); match ( - parse_left_boundary(input), - parse_right_boundary(input), - parse_whole_condition(input), + parse_whole_range(input), + parse_left_range_boundary(input), + parse_right_range_boundary(input), ) { - (Ok(left_boundary), Err(_), Err(_)) => { - let (_, (value, unit, _)) = left_boundary; - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value, unit)).into_seconds(), - side: BoundarySide::Left, - }); - } - (Err(_), Ok(right_boundary), Err(_)) => { - let (_, (_, value, unit)) = right_boundary; - - condition.set_condition(SingleCondition { - value: DateUnitKind::from((value, unit)).into_seconds(), - side: BoundarySide::Right, - }); - } - (_, _, Ok(whole_condition)) => { + (Ok(whole_condition), _, _) => { let (_, (value_left, unit_left, _, value_right, unit_right)) = whole_condition; if unit_left != unit_right { @@ -82,14 +62,30 @@ impl FromStr for PeriodRange { }); } - condition.set_condition(SingleCondition { + condition.set_condition(SingleRangeCondition { value: DateUnitKind::from((value_left, unit_left)).into_seconds(), - side: BoundarySide::Left, + side: RangeBoundarySide::Left, }); - condition.set_condition(SingleCondition { + condition.set_condition(SingleRangeCondition { value: DateUnitKind::from((value_right, unit_right)).into_seconds(), - side: BoundarySide::Right, + side: RangeBoundarySide::Right, + }); + } + (_, Ok(left_boundary), _) => { + let (_, (value, unit, _)) = left_boundary; + + condition.set_condition(SingleRangeCondition { + value: DateUnitKind::from((value, unit)).into_seconds(), + side: RangeBoundarySide::Left, + }); + } + (_, _, Ok(right_boundary)) => { + let (_, (_, value, unit)) = right_boundary; + + condition.set_condition(SingleRangeCondition { + value: DateUnitKind::from((value, unit)).into_seconds(), + side: RangeBoundarySide::Right, }); } _ => { @@ -124,7 +120,7 @@ mod tests { use super::*; #[test] - fn parse_whole_period_condition_to_range_passes() { + fn test_parse_whole_period_condition_to_range_passes() { let condition = "1d..7d"; let range = PeriodRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" @@ -135,7 +131,7 @@ mod tests { } #[test] - fn parse_left_period_condition_to_range_passes() { + fn test_parse_left_period_condition_to_range_passes() { let condition = "1d.."; let range = PeriodRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" @@ -146,7 +142,7 @@ mod tests { } #[test] - fn parse_right_period_condition_to_range_passes() { + fn test_parse_right_period_condition_to_range_passes() { let condition = "..1d"; let range = PeriodRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" @@ -158,17 +154,15 @@ mod tests { #[test] #[should_panic] - fn parse_different_units_on_whole_period_condition_to_range_fails() { + fn test_parse_different_units_on_whole_period_condition_to_range_fails() { let condition = "1w..7d"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r""); + let _range = PeriodRange::from_str(condition).unwrap(); } #[test] #[should_panic] - fn parse_non_standard_units_on_whole_period_condition_to_range_fails() { + fn test_parse_non_standard_units_on_whole_period_condition_to_range_fails() { let condition = "1f..7f"; - let range = PeriodRange::from_str(condition).unwrap(); - insta::assert_debug_snapshot!(range, @r""); + let _range = PeriodRange::from_str(condition).unwrap(); } } diff --git a/crates/organize-rs_core/src/parsers/size_range.rs b/crates/organize-rs_core/src/parsers/size_range.rs index 4accd3c..3285419 100644 --- a/crates/organize-rs_core/src/parsers/size_range.rs +++ b/crates/organize-rs_core/src/parsers/size_range.rs @@ -5,20 +5,15 @@ use std::{fmt::Display, ops::Range, str::FromStr}; use serde::Serialize; use serde_with::DeserializeFromStr; use winnow::{ - ascii::alpha0, - combinator::alt, - error::{Error, ErrorKind}, - token::take_while, - IResult, Parser, + error::{Error, ErrorKind}, Parser, }; use byte_unit::Byte; use crate::{ - filters::DateUnitKind, parsers::{ - parse_left_boundary, parse_right_boundary, parse_whole_condition, BoundarySide, Condition, - SingleCondition, + parse_left_range_boundary, parse_right_range_boundary, parse_whole_range, Condition, + RangeBoundarySide, SingleRangeCondition, }, }; @@ -46,36 +41,14 @@ impl FromStr for SizeRange { let mut condition = Condition(None, None); match ( - parse_left_boundary(input), - parse_right_boundary(input), - parse_whole_condition(input), + parse_whole_range(input), + parse_left_range_boundary(input), + parse_right_range_boundary(input), ) { - (Ok(left_boundary), Err(_), Err(_)) => { - let (_, (size, unit, _)) = left_boundary; - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { - input: format!("Couldn't convert {size} {unit} to bytes: {err}"), - kind: ErrorKind::Fail, - })?, - side: BoundarySide::Left, - }); - } - (Err(_), Ok(right_boundary), Err(_)) => { - let (_, (_, size, unit)) = right_boundary; - - condition.set_condition(SingleCondition { - value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { - input: format!("Couldn't convert {size} {unit} to bytes: {err}"), - kind: ErrorKind::Fail, - })?, - side: BoundarySide::Right, - }); - } - (_, _, Ok(whole_condition)) => { + (Ok(whole_condition), _, _) => { let (_, (size_left, unit_left, _, size_right, unit_right)) = whole_condition; - condition.set_condition(SingleCondition { + condition.set_condition(SingleRangeCondition { value: Byte::from_str(format!("{size_left} {unit_left}")).map_err(|err| { Error { input: format!( @@ -84,10 +57,10 @@ impl FromStr for SizeRange { kind: ErrorKind::Fail, } })?, - side: BoundarySide::Left, + side: RangeBoundarySide::Left, }); - condition.set_condition(SingleCondition { + condition.set_condition(SingleRangeCondition { value: Byte::from_str(format!("{size_right} {unit_right}")).map_err(|err| { Error { input: format!( @@ -96,7 +69,29 @@ impl FromStr for SizeRange { kind: ErrorKind::Fail, } })?, - side: BoundarySide::Right, + side: RangeBoundarySide::Right, + }); + } + (_, Ok(left_boundary), _) => { + let (_, (size, unit, _)) = left_boundary; + + condition.set_condition(SingleRangeCondition { + value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { + input: format!("Couldn't convert {size} {unit} to bytes: {err}"), + kind: ErrorKind::Fail, + })?, + side: RangeBoundarySide::Left, + }); + } + (_, _, Ok(right_boundary)) => { + let (_, (_, size, unit)) = right_boundary; + + condition.set_condition(SingleRangeCondition { + value: Byte::from_str(format!("{size} {unit}")).map_err(|err| Error { + input: format!("Couldn't convert {size} {unit} to bytes: {err}"), + kind: ErrorKind::Fail, + })?, + side: RangeBoundarySide::Right, }); } _ => { @@ -147,7 +142,7 @@ mod tests { use super::*; #[test] - fn parse_left_size_condition_to_range_passes() { + fn test_parse_left_size_condition_to_range_passes() { let condition = "5.0GiB.."; let range = SizeRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" @@ -158,7 +153,7 @@ mod tests { } #[test] - fn parse_right_size_condition_to_range_passes() { + fn test_parse_right_size_condition_to_range_passes() { let condition = "..0.5GiB"; let range = SizeRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" @@ -169,7 +164,7 @@ mod tests { } #[test] - fn parse_whole_size_condition_to_range_passes() { + fn test_parse_whole_size_condition_to_range_passes() { let condition = "1.5MiB..100.3MB"; let range = SizeRange::from_str(condition).unwrap(); insta::assert_debug_snapshot!(range, @r###" diff --git a/crates/organize-rs_core/src/parsers/template.rs b/crates/organize-rs_core/src/parsers/template.rs new file mode 100644 index 0000000..7c255b4 --- /dev/null +++ b/crates/organize-rs_core/src/parsers/template.rs @@ -0,0 +1,147 @@ +use winnow::{ + combinator::{alt}, + multi::separated0, + sequence::{delimited, separated_pair}, + token::take_while, + IResult, Parser, +}; +use winnow::{ + stream::AsChar, +}; + +pub fn parse_text_incl_underscore_hyphen(input: &str) -> IResult<&str, &str> { + take_while(1.., (AsChar::is_alpha, ('_'), ('-'))).parse_next(input) +} + +pub fn parse_boundaries(input: &str) -> IResult<&str, &str> { + take_while(1.., "{()}").parse_next(input) +} + +pub fn parse_function_boundary(input: &str) -> IResult<&str, &str> { + take_while(1.., "()").parse_next(input) +} + +pub fn parse_template_outer_boundary(input: &str) -> IResult<&str, &str> { + take_while(1.., "{}").parse_next(input) +} + +pub fn parse_template(input: &str) -> IResult<&str, &str> { + (delimited( + parse_template_outer_boundary, + parse_template_content, + parse_template_outer_boundary, + )) + .parse_next(input) +} + +pub fn is_template_boundary(character: char) -> bool { + matches!(character, '{' | '}') +} + +pub fn parse_transform_template(input: &str) -> IResult<&str, (&str, Vec<&str>)> { + let (_, template) = parse_template.parse_next(input)?; + + ( + parse_text_incl_underscore_hyphen, + delimited( + parse_function_boundary, + parse_dot_separated_text, + parse_function_boundary, + ), + ) + .parse_next(template) +} + +pub fn parse_dotted_template(input: &str) -> IResult<&str, Vec<&str>> { + let (_, template) = parse_template.parse_next(input)?; + + (parse_dot_separated_text).parse_next(template) +} + +fn parse_dot_separated_text(input: &str) -> IResult<&str, Vec<&str>> { + // take_while(1.., (AsChar::is_alpha, ('.'))).parse_next(input) + separated0(parse_text_incl_underscore_hyphen, '.').parse_next(input) +} + +fn parse_template_content(input: &str) -> IResult<&str, &str> { + take_while(1.., (AsChar::is_alpha, "._()-%, '")).parse_next(input) +} + +fn parse_strftime_format(input: &str) -> IResult<&str, &str> { + delimited('\'', take_while(1.., (AsChar::is_alpha, "%-")), '\'').parse_next(input) +} + +pub fn parse_strftime_template(input: &str) -> IResult<&str, (&str, Vec<&str>, &str)> { + let (_, template) = parse_template.parse_next(input)?; + + let (remainder, (function, (args, formatting_string))) = ( + parse_text_incl_underscore_hyphen, + delimited( + parse_function_boundary, + separated_pair( + parse_dot_separated_text, + alt(((", "), (","))), + parse_strftime_format, + ), + parse_function_boundary, + ), + ) + .parse_next(template)?; + + Ok((remainder, (function, args, formatting_string))) +} + +#[cfg(test)] +mod tests { + + use rstest::rstest; + + use super::*; + + #[rstest] + #[should_panic] + #[case("{{uppercase(metadata.extension)}}", ("", vec![""], ""))] + #[should_panic] + #[case("{{metadata.extension}}", ("", vec![""], ""))] + #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", ("strftime", vec!["metadata", "date_added"], "%Y-%m-%d"))] + fn test_parse_strftime_template_passes( + #[case] template: &str, + #[case] outcome: (&str, Vec<&str>, &str), + ) { + let (remainder, result) = (parse_strftime_template).parse_next(template).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } + + #[rstest] + #[should_panic] + #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", ("", vec![""]))] + #[should_panic] + #[case("{{metadata.extension}}", ("", vec![""]))] + #[case("{{uppercase(metadata.extension)}}", ("uppercase", vec!["metadata", "extension"]))] + #[case("{{lowercase(metadata.name)}}", ("lowercase", vec!["metadata", "name"]))] + fn test_parse_transform_template_passes( + #[case] template: &str, + #[case] outcome: (&str, Vec<&str>), + ) { + let (remainder, result) = (parse_transform_template).parse_next(template).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } + + #[rstest] + #[should_panic] + #[case("{{uppercase(metadata.extension)}}", vec!["uppercase", "metadata", "extension"])] + #[should_panic] + #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", vec![""])] + #[case("{{utility.counter}}", vec!["utility", "counter"])] + #[case("{{metadata.extension}}", vec!["metadata", "extension"])] + #[case("{{metadata.created.year}}", vec!["metadata", "created", "year"])] + #[case("{{content.customer}}", vec!["content", "customer"])] + #[case("{{metadata.last_modified.year}}", vec!["metadata", "last_modified", "year"])] + fn test_parse_dotted_template_passes(#[case] template: &str, #[case] outcome: Vec<&str>) { + let (remainder, result) = (parse_dotted_template).parse_next(template).unwrap(); + assert!(remainder.is_empty()); + assert_eq!(result, outcome); + } +} diff --git a/crates/organize-rs_core/src/templating.rs b/crates/organize-rs_core/src/templating.rs index b4ef072..e71172d 100644 --- a/crates/organize-rs_core/src/templating.rs +++ b/crates/organize-rs_core/src/templating.rs @@ -1,104 +1,302 @@ -use displaydoc::Display; -// use filetime::FileTime; -use serde::{Deserialize, Serialize}; - -#[cfg(feature = "cli")] -use clap::ValueEnum; - -// use winnow::{ -// ascii::alpha0, -// combinator::alt, -// error::{Error, ErrorKind}, -// token::take_while, -// IResult, Parser, -// }; - -// // Display -// #[derive(Debug, Clone)] -// pub enum TemplateActionKind { -// UpperCase(String), -// LowerCase(String), -// CamelCase(String), -// SnakeCase(String), -// KebabCase(String), -// Counter(usize), -// Date(FileTime), -// } - -/// Support template strings -#[cfg_attr(feature = "cli", derive(ValueEnum))] -#[derive(Debug, Clone, Copy, Deserialize, Serialize, Display)] -pub enum TemplateStringKind { - /// {{filename}} - Filename, - /// {{counter}} - Counter, - /// {{extension}} +use std::str::FromStr; + +use winnow::error::Error; + +use crate::parsers::template::{ + parse_dotted_template, parse_strftime_template, parse_transform_template, +}; + +type FunctionClosure = Box String>; +type FormatString = String; + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TransformationKind { + /// uppercase + UpperCase, + /// lowercase + LowerCase, + /// camelcase + CamelCase, + /// snakecase + SnakeCase, + /// kebabcase + KebabCase, + /// strftime + DateTime, +} + +impl FromStr for TransformationKind { + type Err = String; + + fn from_str(s: &str) -> Result { + Ok(match s { + "uppercase" => Self::UpperCase, + "lowercase" => Self::LowerCase, + "camelcase" => Self::CamelCase, + "snakecase" => Self::SnakeCase, + "kebabcase" => Self::KebabCase, + "strftime" => Self::DateTime, + _ => return Err("FunctionKind not recognized.".to_string()), + }) + } +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum MetaDataKind { + DateAdded(DateAttributeArgKind), + Created(DateAttributeArgKind), + LastModified(DateAttributeArgKind), + Name, Extension, - /// {{date}} - Date, } -impl TemplateStringKind { - /// Returns `true` if [`TemplateStrings`] is [`Filename`]. - /// - /// [`Filename`]: TemplateStrings::Filename - #[must_use] - pub fn is_filename(&self) -> bool { - matches!(self, Self::Filename) +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum DateAttributeArgKind { + Year, + Month, + Day, + NotSet, +} + +impl Default for DateAttributeArgKind { + fn default() -> Self { + Self::NotSet + } +} + +impl FromStr for DateAttributeArgKind { + type Err = String; + + fn from_str(s: &str) -> Result { + let result = match s { + "year" => Self::Year, + "month" => Self::Month, + "day" => Self::Day, + _ => Self::default(), + }; + + Ok(result) } +} - /// Returns `true` if [`TemplateStrings`] is [`Counter`]. - /// - /// [`Counter`]: TemplateStrings::Counter - #[must_use] - pub fn is_counter(&self) -> bool { - matches!(self, Self::Counter) +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum UtilityKind { + Counter, +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TemplateFeatureKind { + Utility(UtilityKind), + MetaData(MetaDataKind), + Content(String), + NotRecognized, +} + +impl From<&[&str]> for TemplateFeatureKind { + fn from(value: &[&str]) -> Self { + match value { + ["utility", "counter"] => Self::Utility(UtilityKind::Counter), + ["metadata", "extension"] => Self::MetaData(MetaDataKind::Extension), + ["metadata", "date_added"] => { + Self::MetaData(MetaDataKind::DateAdded(DateAttributeArgKind::NotSet)) + } + ["metadata", "date_added", date] => Self::MetaData(MetaDataKind::DateAdded( + DateAttributeArgKind::from_str(date).expect("conversion to date shouldn't fail"), + )), + ["metadata", "created"] => { + Self::MetaData(MetaDataKind::Created(DateAttributeArgKind::NotSet)) + } + ["metadata", "created", date] => Self::MetaData(MetaDataKind::Created( + DateAttributeArgKind::from_str(date).expect("conversion to date shouldn't fail"), + )), + ["metadata", "last_modified"] => { + Self::MetaData(MetaDataKind::LastModified(DateAttributeArgKind::NotSet)) + } + ["metadata", "last_modified", date] => Self::MetaData(MetaDataKind::LastModified( + DateAttributeArgKind::from_str(date).expect("conversion to date shouldn't fail"), + )), + ["metadata", "name"] => Self::MetaData(MetaDataKind::Name), + ["content", last] => Self::Content(last.to_string()), + _ => Self::NotRecognized, + } } +} + +#[non_exhaustive] +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum TemplateKind { + Dotted { + data: TemplateFeatureKind, + }, + FormattedTransformation { + kind: TransformationKind, + data: TemplateFeatureKind, + format: FormatString, + }, + Transformation { + kind: TransformationKind, + data: TemplateFeatureKind, + }, +} + +impl TemplateKind { + pub fn get_template(&self) -> String { + match self { + TemplateKind::Dotted { data: _ } => todo!(), + TemplateKind::FormattedTransformation { + kind: _, + data: _, + format: _, + } => todo!(), + TemplateKind::Transformation { kind: _, data: _ } => todo!(), + } + } +} - /// Returns `true` if [`TemplateStrings`] is [`Extension`]. - /// - /// [`Extension`]: TemplateStrings::Extension - #[must_use] - pub fn is_extension(&self) -> bool { - matches!(self, Self::Extension) +impl FromStr for TemplateKind { + type Err = Error; + + fn from_str(input: &str) -> Result { + let result = match ( + parse_transform_template(input), + parse_strftime_template(input), + parse_dotted_template(input), + ) { + (Ok((_, (kind, items))), _, _) => Self::Transformation { + kind: TransformationKind::from_str(kind).map_err(|err| Error { + input: err, + kind: winnow::error::ErrorKind::Fail, + })?, + data: TemplateFeatureKind::from(items.as_slice()), + }, + (_, Ok((_, (kind, items, format))), _) => Self::FormattedTransformation { + kind: TransformationKind::from_str(kind).map_err(|err| Error { + input: err, + kind: winnow::error::ErrorKind::Fail, + })?, + data: TemplateFeatureKind::from(items.as_slice()), + format: format.to_string(), + }, + (_, _, Ok((_, path))) => Self::Dotted { + data: TemplateFeatureKind::from(path.as_slice()), + }, + _ => { + return Err(Error { + input: format!("Not a valid template: {input}"), + kind: winnow::error::ErrorKind::Verify, + }) + } + }; + + Ok(result) } } -// // pub fn parse_left_boundary(input: &str) -> IResult<&str, (f64, &str, &str)> { -// // (parse_digits, parse_units, parse_boundary).parse_next(input) -// // } - -// pub fn parse_units(input: &str) -> IResult<&str, &str> { -// alpha0(input) -// } -// #[cfg(test)] -// mod tests { -// use std::path::PathBuf; - -// #[test] -// fn test_unpacking_multiple_template_strings_passes() { -// let extension = "toml"; -// let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(extension)}}\"#); -// let parts = example.components(); -// parts.into_iter().map(|component| { -// component.as_os_str().to_str().map(|string| { -// let left = string.find("{{"); -// let right = string.rfind("}}"); - -// let (Some(left), Some(right)) = (left, right) else { -// return string -// }; - -// // we do `left+2..right` here, because we actually extract -// // the outer most template directly -// let template = &string[left + 2..right]; - -// assert_eq!(template, "uppercase(extension)"); - -// let left = template.find("{{"); -// let right = template.rfind("}}"); -// }) -// }) -// } -// } +#[cfg(test)] +mod tests { + use std::{path::PathBuf, str::FromStr}; + + use itertools::Itertools; + + use super::*; + + #[test] + fn test_parsing_uppercase_extension_templatekind_passes() { + let template = "{{uppercase(metadata.extension)}}"; + + let template_kind = TemplateKind::from_str(template).unwrap(); + + assert_eq!( + template_kind, + TemplateKind::Transformation { + kind: TransformationKind::UpperCase, + data: TemplateFeatureKind::MetaData(MetaDataKind::Extension), + } + ); + } + + #[test] + fn test_parsing_counter_templatekind_passes() { + let template = "{{utility.counter}}"; + + let template_kind = TemplateKind::from_str(template).unwrap(); + + assert_eq!( + template_kind, + TemplateKind::Dotted { + data: TemplateFeatureKind::Utility(UtilityKind::Counter) + } + ); + } + + #[test] + fn test_parsing_content_templatekind_passes() { + let template = "{{content.customer}}"; + + let template_kind = TemplateKind::from_str(template).unwrap(); + + assert_eq!( + template_kind, + TemplateKind::Dotted { + data: TemplateFeatureKind::Content("customer".to_string()) + } + ); + } + + #[test] + fn test_parsing_metadata_templatekind_passes() { + let template = "{{metadata.last_modified.year}}"; + + let template_kind = TemplateKind::from_str(template).unwrap(); + + assert_eq!( + template_kind, + TemplateKind::Dotted { + data: TemplateFeatureKind::MetaData(MetaDataKind::LastModified( + DateAttributeArgKind::Year + )) + } + ); + } + + #[test] + fn test_parsing_strftime_templatekind_passes() { + let template = "{{strftime(metadata.date_added, '%Y-%m-%d')}}"; + + let template_kind = TemplateKind::from_str(template).unwrap(); + + assert_eq!( + template_kind, + TemplateKind::FormattedTransformation { + kind: TransformationKind::DateTime, + data: TemplateFeatureKind::MetaData(MetaDataKind::DateAdded( + DateAttributeArgKind::NotSet + )), + format: FormatString::from("%Y-%m-%d") + } + ); + } + + #[test] + fn test_unpacking_multiple_template_strings_passes() { + let entry = PathBuf::from(r#"C:\Users\dailyuse\Desktop\test.pdf"#); + let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(entry.extension)}}\"#); + + // let components = example.components(); + // let new_path = components.into_iter().map(|component| { + // component.as_os_str().to_str().map(|string| { + // let Ok(template) = TemplateKind::from_str(string) else { + // return string + // }; + + // string + + // }) + // }).collect_vec(); + } +} diff --git a/src/lib.rs b/src/lib.rs index aa0fcb6..f64ba62 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -37,4 +37,4 @@ pub mod scripting; // re-exports for documentation purposes pub use organize_rs_core::actions::{conflicts::ConflictResolutionKind, ActionKind}; pub use organize_rs_core::filters::FilterKind; -pub use organize_rs_core::templating::TemplateStringKind; +pub use organize_rs_core::templating::TemplateKind; From d69cdef894f10851a8d85b293f42cda6ede67953 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Fri, 9 Jun 2023 13:04:46 +0200 Subject: [PATCH 06/12] feat(version): add version to config file --- Cargo.lock | 2 + Cargo.toml | 3 + crates/organize-rs_core/Cargo.toml | 1 + crates/organize-rs_core/config/config.ncl | 0 .../config/config_template.yaml | 2 + crates/organize-rs_core/src/config.rs | 18 +++++ crates/organize-rs_core/src/templating.rs | 81 ++++++++++++++----- 7 files changed, 88 insertions(+), 19 deletions(-) create mode 100644 crates/organize-rs_core/config/config.ncl diff --git a/Cargo.lock b/Cargo.lock index 6f16d55..580ef14 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1222,6 +1222,7 @@ dependencies = [ "regex", "rhai", "ron 0.8.0", + "semver", "serde", "serde_json", "serde_with", @@ -1263,6 +1264,7 @@ dependencies = [ "rstest", "rustdoc-json", "rustup-toolchain", + "semver", "serde", "serde_json", "serde_with", diff --git a/Cargo.toml b/Cargo.toml index a28f652..0becea1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -101,6 +101,8 @@ insta = { version = "1.29.0", features = ["yaml", "ron", "json", "toml"] } # Scripting rhai = { version = "1.14.0", features = ["serde"] } +semver = "1.0.17" + # TODO: Needed Dependencies # czkawka_core = "4.1.0" # + Lazy-regex? @@ -174,6 +176,7 @@ dialoguer = { workspace = true } ron = { workspace = true } open = { workspace = true } serde_with = { workspace = true } +semver = { workspace = true, features = ["serde"] } [dev-dependencies] diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index 29cd332..6b32ec3 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -52,6 +52,7 @@ crossbeam = { workspace = true } ron = { workspace = true } serde_with = { workspace = true } console = { workspace = true } +semver = { workspace = true } [dev-dependencies] rustup-toolchain = "0.1.5" diff --git a/crates/organize-rs_core/config/config.ncl b/crates/organize-rs_core/config/config.ncl new file mode 100644 index 0000000..e69de29 diff --git a/crates/organize-rs_core/config/config_template.yaml b/crates/organize-rs_core/config/config_template.yaml index a1f6428..1878c70 100644 --- a/crates/organize-rs_core/config/config_template.yaml +++ b/crates/organize-rs_core/config/config_template.yaml @@ -1,3 +1,5 @@ +# Version of the config file format, default is `1.0.0` +version: 1.0.0 # Rules you can define for organize to execute rules: # name of a single rule [any string] diff --git a/crates/organize-rs_core/src/config.rs b/crates/organize-rs_core/src/config.rs index f5ff342..54ed1a2 100644 --- a/crates/organize-rs_core/src/config.rs +++ b/crates/organize-rs_core/src/config.rs @@ -12,6 +12,7 @@ use clap::ValueEnum; use displaydoc::Display; use ron::ser::PrettyConfig; +use semver::{BuildMetadata, Prerelease, Version}; use serde::{Deserialize, Serialize}; pub static CONFIG_TEMPLATE_YAML: &str = include_str!("../config/config_template.yaml"); @@ -53,17 +54,34 @@ impl Default for ConfigFileFormat { } } +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct OrganizeConfigVersion(Version); + +impl Default for OrganizeConfigVersion { + fn default() -> Self { + Self(Version { + major: 1, + minor: 0, + patch: 0, + pre: Prerelease::EMPTY, + build: BuildMetadata::EMPTY, + }) + } +} + /// Organize Configuration #[derive(Clone, Debug, Deserialize, Serialize, Default)] // #[serde(deny_unknown_fields)] #[serde(default)] pub struct OrganizeConfig { // aliases: Vec, + version: OrganizeConfigVersion, rules: Rules, } impl Display for OrganizeConfig { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Config-Version: {:?}", self.version)?; for rule in self.rules.iter() { write!(f, "{rule}")?; } diff --git a/crates/organize-rs_core/src/templating.rs b/crates/organize-rs_core/src/templating.rs index e71172d..49cbd43 100644 --- a/crates/organize-rs_core/src/templating.rs +++ b/crates/organize-rs_core/src/templating.rs @@ -142,6 +142,7 @@ pub enum TemplateKind { kind: TransformationKind, data: TemplateFeatureKind, }, + Uninitialized } impl TemplateKind { @@ -154,6 +155,7 @@ impl TemplateKind { format: _, } => todo!(), TemplateKind::Transformation { kind: _, data: _ } => todo!(), + TemplateKind::Uninitialized => todo!(), } } } @@ -199,12 +201,71 @@ impl FromStr for TemplateKind { #[cfg(test)] mod tests { - use std::{path::PathBuf, str::FromStr}; + use std::{ + borrow::{Borrow}, + ffi::OsStr, + path::{PathBuf}, + str::FromStr, + }; use itertools::Itertools; use super::*; + #[test] + fn test_extraction_of_template_passes() { + let extension = "pdf".to_string(); + let example = + PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(metadata.extension)}}\"#); + + let mut item_position = 0; + let mut template_kind = TemplateKind::Uninitialized; + + let mut editable = example + .iter() + .enumerate() + .map(|(idx, component)| { + let Ok(template) = TemplateKind::from_str(component.to_string_lossy().borrow()) else { + return component + }; + + assert_eq!( + template, + TemplateKind::Transformation { + kind: TransformationKind::UpperCase, + data: TemplateFeatureKind::MetaData(MetaDataKind::Extension), + } + ); + + template_kind = template; + item_position = idx; + + component + }) + .collect_vec(); + + let uppercase = extension.to_uppercase(); + editable[item_position] = OsStr::new(&uppercase); + let path = editable.iter().collect::(); + + assert_eq!(path, PathBuf::from(r#"C:\Users\dailyuse\Desktop\PDF"#)) + } + + // #[test] + // fn test_template_on_path_end_passes() { + // let entry = PathBuf::from(r#"C:\Users\dailyuse\Desktop\test.pdf"#); + // let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(entry.extension)}}\"#); + + // let extension = entry.extension().unwrap(); + // let extension = extension.to_ascii_uppercase(); + + // let mut out_path = example; + // out_path.pop(); + // out_path.push(extension); + + // assert_eq!(out_path, PathBuf::from(r#"C:\Users\dailyuse\Desktop\PDF\"#)) + // } + #[test] fn test_parsing_uppercase_extension_templatekind_passes() { let template = "{{uppercase(metadata.extension)}}"; @@ -281,22 +342,4 @@ mod tests { } ); } - - #[test] - fn test_unpacking_multiple_template_strings_passes() { - let entry = PathBuf::from(r#"C:\Users\dailyuse\Desktop\test.pdf"#); - let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(entry.extension)}}\"#); - - // let components = example.components(); - // let new_path = components.into_iter().map(|component| { - // component.as_os_str().to_str().map(|string| { - // let Ok(template) = TemplateKind::from_str(string) else { - // return string - // }; - - // string - - // }) - // }).collect_vec(); - } } From 75f17d7bb3193261b3e27ce62e91aa18693f196d Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Wed, 14 Jun 2023 11:44:27 +0200 Subject: [PATCH 07/12] feat(aliases): add unit test for de-/serialization --- crates/organize-rs_core/src/aliases.rs | 94 +++++++++++++++++++++++--- 1 file changed, 85 insertions(+), 9 deletions(-) diff --git a/crates/organize-rs_core/src/aliases.rs b/crates/organize-rs_core/src/aliases.rs index 64d2c2f..4b3c426 100644 --- a/crates/organize-rs_core/src/aliases.rs +++ b/crates/organize-rs_core/src/aliases.rs @@ -3,12 +3,14 @@ use serde::{Deserialize, Serialize}; +use crate::aliases; + #[derive(Debug, Clone, Deserialize, Serialize)] pub enum ItemKind { - #[serde(rename = "ext")] + #[serde(rename = "extension")] Extension, #[serde(rename = "folder")] - Folder, + Location, } impl ItemKind { @@ -25,27 +27,38 @@ impl ItemKind { /// [`Folder`]: ItemKind::Folder #[must_use] pub fn is_folder(&self) -> bool { - matches!(self, Self::Folder) + matches!(self, Self::Location) } } impl Default for ItemKind { fn default() -> Self { - Self::Folder + Self::Location } } -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Alias(String); +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +#[serde(transparent)] +pub struct Aliases(Vec); -#[derive(Debug, Clone, Deserialize, Serialize)] -pub struct Reference { +impl Aliases { + pub fn new() -> Self { + Self::default() + } + + pub fn with_aliases(aliases: Vec) -> Self { + Self(aliases) + } +} + +#[derive(Debug, Clone, Deserialize, Serialize, Default)] +pub struct Alias { name: String, kind: ItemKind, items: Vec, } -impl Reference { +impl Alias { pub fn name(&self) -> &str { self.name.as_ref() } @@ -63,3 +76,66 @@ impl Reference { self.items.len() == 0 } } + +#[cfg(test)] +mod tests { + + use insta::assert_yaml_snapshot; + + use super::*; + + #[test] + fn test_yaml_aliases_parsing_passed() { + let yaml = r#" + - name: downloads_folder + kind: folder + items: + - ~/Downloads/ + - name: img_ext + kind: extension + items: + - png + - jpg + - jpeg + - gif + - svg + - jfif + - tiff + - bmp + - webp + "#; + + let _aliases: Aliases = serde_yaml::from_str(yaml).unwrap(); + } + + #[test] + fn test_yaml_aliases_serialization_passes() { + let alias = Alias { + name: "Config extensions".to_string(), + kind: ItemKind::Extension, + items: vec!["toml".to_string(), "json".to_string()], + }; + + let alias2 = Alias { + name: "Image extensions".to_string(), + kind: ItemKind::Extension, + items: vec!["jpg".to_string(), "png".to_string()], + }; + + let aliases = Aliases::with_aliases(vec![alias, alias2]); + + assert_yaml_snapshot!(aliases, @r###" + --- + - name: Config extensions + kind: extension + items: + - toml + - json + - name: Image extensions + kind: extension + items: + - jpg + - png + "###); + } +} From cb4aae5cbde076197f3a67b8221e3b451b17be9b Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Fri, 7 Jul 2023 18:18:11 +0200 Subject: [PATCH 08/12] refactor: before ports/adapters --- Cargo.lock | 2 + Cargo.toml | 3 +- TODO.md | 48 +++++++++++++++++-- crates/organize-rs_core/Cargo.toml | 2 + crates/organize-rs_core/config/config.ncl | 44 +++++++++++++++++ .../config/config_template.yaml | 14 +++++- crates/organize-rs_core/src/config.rs | 12 ++++- .../organize-rs_core/src/parsers/template.rs | 43 ++++++++--------- crates/organize-rs_core/src/templating.rs | 47 ++++++++++++++---- 9 files changed, 177 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 580ef14..28fd722 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1205,6 +1205,7 @@ name = "organize-rs" version = "0.4.1" dependencies = [ "abscissa_core", + "aho-corasick", "anyhow", "chrono", "clap", @@ -1236,6 +1237,7 @@ dependencies = [ name = "organize-rs_core" version = "0.2.3" dependencies = [ + "aho-corasick", "byte-unit", "chrono", "clap", diff --git a/Cargo.toml b/Cargo.toml index 0becea1..c400764 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,7 @@ chrono = { version = "0.4.26", default-features = false, features = [ jwalk = "0.8.1" regex = "1.8.4" +aho-corasick = "1.0.2" abscissa_core = "0.7.0" itertools = "0.10.5" @@ -91,7 +92,6 @@ serde_json = "1.0.96" # dev dependencies tempfile = "3.5.0" -aho-corasick = "1.0" rstest = "0.17" quickcheck = "1" quickcheck_macros = "1" @@ -177,6 +177,7 @@ ron = { workspace = true } open = { workspace = true } serde_with = { workspace = true } semver = { workspace = true, features = ["serde"] } +aho-corasick = { workspace = true } [dev-dependencies] diff --git a/TODO.md b/TODO.md index 2654af7..839c32b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,46 @@ # TODO +## Notes + +- Enums: +- FS actions impl: +- Template impl + - + - +- Duplicates + - + - + +## Templating + +```rust + fn call_command(tpe: FileType, id: &Id, filename: &Path, command: &str) -> RusticResult<()> { + let id = id.to_hex(); + let patterns = &["%file", "%type", "%id"]; + let ac = AhoCorasick::new(patterns).map_err(LocalErrorKind::FromAhoCorasick)?; + let replace_with = &[filename.to_str().unwrap(), tpe.into(), id.as_str()]; + let actual_command = ac.replace_all(command, replace_with); + debug!("calling {actual_command}..."); + let commands = parse_command::<()>(&actual_command) + .map_err(LocalErrorKind::FromNomError)? + .1; + let status = Command::new(commands[0]) + .args(&commands[1..]) + .status() + .map_err(LocalErrorKind::CommandExecutionFailed)?; + if !status.success() { + return Err(LocalErrorKind::CommandNotSuccessful { + file_name: replace_with[0].to_owned(), + file_type: replace_with[1].to_owned(), + id: replace_with[2].to_owned(), + status, + } + .into()); + } + Ok(()) + } +``` + ## Next 1. update screenshots and Readme.md @@ -17,8 +58,9 @@ ```yaml aliases: - - !my_music_folders - paths: + - !alias + name: my_music_folders + paths: - path1 - path2 - etc @@ -28,7 +70,7 @@ ```yaml locations: - - {{my_music_folders}} + - {{alias.my_music_folders}} ``` - check places where it is applicable diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index 6b32ec3..a24dc34 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -53,6 +53,8 @@ ron = { workspace = true } serde_with = { workspace = true } console = { workspace = true } semver = { workspace = true } +aho-corasick = { workspace = true } + [dev-dependencies] rustup-toolchain = "0.1.5" diff --git a/crates/organize-rs_core/config/config.ncl b/crates/organize-rs_core/config/config.ncl index e69de29..89b8c19 100644 --- a/crates/organize-rs_core/config/config.ncl +++ b/crates/organize-rs_core/config/config.ncl @@ -0,0 +1,44 @@ +# WIP +version = "1.0.0", +aliases = [{}], +rules = [{ + name = "Empty file", + enabled = true, + locations = [{ + recursive = { + path = "crates\organize-rs_core\tests\fixtures\filters\empty_file", + max_depth = 1, + target = "Files" + } + }], + filter_groups = [ + { + filters = [ + "empty" = {} + ], + results = "include", + match = "all", + } + ], + actions = [ + { + mode = "preview", + action = { + "trash" + } + }, + { + mode = "preview", + action = { + "echo" + msg = "test" + } + } + ], + tags: [ + { + + } + ] + +}], \ No newline at end of file diff --git a/crates/organize-rs_core/config/config_template.yaml b/crates/organize-rs_core/config/config_template.yaml index 1878c70..4c9eeaf 100644 --- a/crates/organize-rs_core/config/config_template.yaml +++ b/crates/organize-rs_core/config/config_template.yaml @@ -1,5 +1,17 @@ # Version of the config file format, default is `1.0.0` version: 1.0.0 +# Custom-defined aliases to be used within this config file +aliases: + - name: downloads_folder + kind: folder + items: + - ~/Downloads/ + - name: images + kind: extension + items: + - png + - jpg + - jpeg # Rules you can define for organize to execute rules: # name of a single rule [any string] @@ -14,7 +26,7 @@ rules: # `locations` is a list where each item is either # [!recursive, !non_recursive, !default_settings] - !recursive - path: crates\organize-rs_core\tests\fixtures\filters\empty_file + path: "{alias.downloads_folder}" # depth for this location, seen from `path` # how deep organize should traverse the file system max_depth: 1 diff --git a/crates/organize-rs_core/src/config.rs b/crates/organize-rs_core/src/config.rs index 54ed1a2..0af1d0c 100644 --- a/crates/organize-rs_core/src/config.rs +++ b/crates/organize-rs_core/src/config.rs @@ -1,6 +1,7 @@ //! organize-rs config use crate::{ + aliases::Aliases, error::{ConfigErrorKind, OrganizeResult}, rules::{Rule, Rules}, }; @@ -74,7 +75,7 @@ impl Default for OrganizeConfigVersion { // #[serde(deny_unknown_fields)] #[serde(default)] pub struct OrganizeConfig { - // aliases: Vec, + aliases: Aliases, version: OrganizeConfigVersion, rules: Rules, } @@ -214,6 +215,8 @@ mod tests { config.add_rule(rule); insta::assert_yaml_snapshot!(config, @r###" --- + aliases: [] + version: 1.0.0 rules: - name: "" tags: [] @@ -223,6 +226,9 @@ mod tests { actions: [] "###); insta::assert_toml_snapshot!(config, @r###" + aliases = [] + version = '1.0.0' + [[rules]] name = '' tags = [] @@ -233,6 +239,8 @@ mod tests { "###); insta::assert_json_snapshot!(config, @r###" { + "aliases": [], + "version": "1.0.0", "rules": [ { "name": "", @@ -247,6 +255,8 @@ mod tests { "###); insta::assert_ron_snapshot!(config, @r###" OrganizeConfig( + aliases: [], + version: OrganizeConfigVersion("1.0.0"), rules: [ rule( name: "", diff --git a/crates/organize-rs_core/src/parsers/template.rs b/crates/organize-rs_core/src/parsers/template.rs index 7c255b4..57512c8 100644 --- a/crates/organize-rs_core/src/parsers/template.rs +++ b/crates/organize-rs_core/src/parsers/template.rs @@ -1,20 +1,18 @@ +use winnow::stream::AsChar; use winnow::{ - combinator::{alt}, + combinator::alt, multi::separated0, sequence::{delimited, separated_pair}, token::take_while, IResult, Parser, }; -use winnow::{ - stream::AsChar, -}; pub fn parse_text_incl_underscore_hyphen(input: &str) -> IResult<&str, &str> { take_while(1.., (AsChar::is_alpha, ('_'), ('-'))).parse_next(input) } pub fn parse_boundaries(input: &str) -> IResult<&str, &str> { - take_while(1.., "{()}").parse_next(input) + take_while(1.., "{()} ").parse_next(input) } pub fn parse_function_boundary(input: &str) -> IResult<&str, &str> { @@ -22,7 +20,7 @@ pub fn parse_function_boundary(input: &str) -> IResult<&str, &str> { } pub fn parse_template_outer_boundary(input: &str) -> IResult<&str, &str> { - take_while(1.., "{}").parse_next(input) + take_while(1.., "{ }").parse_next(input) } pub fn parse_template(input: &str) -> IResult<&str, &str> { @@ -34,10 +32,6 @@ pub fn parse_template(input: &str) -> IResult<&str, &str> { .parse_next(input) } -pub fn is_template_boundary(character: char) -> bool { - matches!(character, '{' | '}') -} - pub fn parse_transform_template(input: &str) -> IResult<&str, (&str, Vec<&str>)> { let (_, template) = parse_template.parse_next(input)?; @@ -100,10 +94,10 @@ mod tests { #[rstest] #[should_panic] - #[case("{{uppercase(metadata.extension)}}", ("", vec![""], ""))] + #[case("{uppercase(metadata.extension)}", ("", vec![""], ""))] #[should_panic] - #[case("{{metadata.extension}}", ("", vec![""], ""))] - #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", ("strftime", vec!["metadata", "date_added"], "%Y-%m-%d"))] + #[case("{metadata.extension}", ("", vec![""], ""))] + #[case("{strftime(metadata.date_added, '%Y-%m-%d')}", ("strftime", vec!["metadata", "date_added"], "%Y-%m-%d"))] fn test_parse_strftime_template_passes( #[case] template: &str, #[case] outcome: (&str, Vec<&str>, &str), @@ -115,11 +109,11 @@ mod tests { #[rstest] #[should_panic] - #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", ("", vec![""]))] + #[case("{strftime(metadata.date_added, '%Y-%m-%d')}", ("", vec![""]))] #[should_panic] - #[case("{{metadata.extension}}", ("", vec![""]))] - #[case("{{uppercase(metadata.extension)}}", ("uppercase", vec!["metadata", "extension"]))] - #[case("{{lowercase(metadata.name)}}", ("lowercase", vec!["metadata", "name"]))] + #[case("{metadata.extension}", ("", vec![""]))] + #[case("{uppercase(metadata.extension)}", ("uppercase", vec!["metadata", "extension"]))] + #[case("{lowercase(metadata.name)}", ("lowercase", vec!["metadata", "name"]))] fn test_parse_transform_template_passes( #[case] template: &str, #[case] outcome: (&str, Vec<&str>), @@ -131,16 +125,17 @@ mod tests { #[rstest] #[should_panic] - #[case("{{uppercase(metadata.extension)}}", vec!["uppercase", "metadata", "extension"])] + #[case("{uppercase(metadata.extension)}", vec!["uppercase", "metadata", "extension"])] #[should_panic] - #[case("{{strftime(metadata.date_added, '%Y-%m-%d')}}", vec![""])] - #[case("{{utility.counter}}", vec!["utility", "counter"])] - #[case("{{metadata.extension}}", vec!["metadata", "extension"])] - #[case("{{metadata.created.year}}", vec!["metadata", "created", "year"])] - #[case("{{content.customer}}", vec!["content", "customer"])] - #[case("{{metadata.last_modified.year}}", vec!["metadata", "last_modified", "year"])] + #[case("{strftime(metadata.date_added, '%Y-%m-%d')}", vec![""])] + #[case("{utility.counter}", vec!["utility", "counter"])] + #[case("{metadata.extension}", vec!["metadata", "extension"])] + #[case("{metadata.created.year}", vec!["metadata", "created", "year"])] + #[case("{content.customer}", vec!["content", "customer"])] + #[case("{metadata.last_modified.year}", vec!["metadata", "last_modified", "year"])] fn test_parse_dotted_template_passes(#[case] template: &str, #[case] outcome: Vec<&str>) { let (remainder, result) = (parse_dotted_template).parse_next(template).unwrap(); + assert!(remainder.is_empty()); assert_eq!(result, outcome); } diff --git a/crates/organize-rs_core/src/templating.rs b/crates/organize-rs_core/src/templating.rs index 49cbd43..ef8d23e 100644 --- a/crates/organize-rs_core/src/templating.rs +++ b/crates/organize-rs_core/src/templating.rs @@ -1,14 +1,28 @@ -use std::str::FromStr; +use std::{str::FromStr, collections::HashMap}; use winnow::error::Error; use crate::parsers::template::{ parse_dotted_template, parse_strftime_template, parse_transform_template, }; +use aho_corasick::{AhoCorasick, PatternID}; type FunctionClosure = Box String>; type FormatString = String; +type Transformation = fn(String) -> String; + +use once_cell::sync::Lazy; + +// static TEMPLATE_PATTERNS: Lazy> = Lazy::new(|| { +// println!("initializing"); +// let mut m = HashMap::new(); +// m.insert("metadata.extension", "Spica".to_string()); +// m.insert(74, "Hoyten".to_string()); +// m +// }); + + #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq)] pub enum TransformationKind { @@ -37,7 +51,7 @@ impl FromStr for TransformationKind { "snakecase" => Self::SnakeCase, "kebabcase" => Self::KebabCase, "strftime" => Self::DateTime, - _ => return Err("FunctionKind not recognized.".to_string()), + _ => return Err("TransformationKind not recognized.".to_string()), }) } } @@ -94,6 +108,7 @@ pub enum TemplateFeatureKind { Utility(UtilityKind), MetaData(MetaDataKind), Content(String), + // TODO: Aliases NotRecognized, } @@ -212,11 +227,27 @@ mod tests { use super::*; + #[test] + fn test_aho_corasick_template_passes() { + let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(metadata.extension)}}\"#); + let patterns = &["metadata.extension"]; + + let ac = AhoCorasick::new(patterns).unwrap(); + + let replace_with = &["toml"]; + + let path = ac.replace_all(example.to_str().unwrap(), replace_with); + + dbg!(&path); + assert_eq!(path, r#"C:\Users\dailyuse\Desktop\{ uppercase(toml) }\"# ) + + } + #[test] fn test_extraction_of_template_passes() { let extension = "pdf".to_string(); let example = - PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(metadata.extension)}}\"#); + PathBuf::from(r#"C:\Users\dailyuse\Desktop\{ uppercase(metadata.extension) }\"#); let mut item_position = 0; let mut template_kind = TemplateKind::Uninitialized; @@ -268,7 +299,7 @@ mod tests { #[test] fn test_parsing_uppercase_extension_templatekind_passes() { - let template = "{{uppercase(metadata.extension)}}"; + let template = "{uppercase(metadata.extension)}"; let template_kind = TemplateKind::from_str(template).unwrap(); @@ -283,7 +314,7 @@ mod tests { #[test] fn test_parsing_counter_templatekind_passes() { - let template = "{{utility.counter}}"; + let template = "{utility.counter}"; let template_kind = TemplateKind::from_str(template).unwrap(); @@ -297,7 +328,7 @@ mod tests { #[test] fn test_parsing_content_templatekind_passes() { - let template = "{{content.customer}}"; + let template = "{content.customer}"; let template_kind = TemplateKind::from_str(template).unwrap(); @@ -311,7 +342,7 @@ mod tests { #[test] fn test_parsing_metadata_templatekind_passes() { - let template = "{{metadata.last_modified.year}}"; + let template = "{metadata.last_modified.year}"; let template_kind = TemplateKind::from_str(template).unwrap(); @@ -327,7 +358,7 @@ mod tests { #[test] fn test_parsing_strftime_templatekind_passes() { - let template = "{{strftime(metadata.date_added, '%Y-%m-%d')}}"; + let template = "{strftime(metadata.date_added, '%Y-%m-%d')}"; let template_kind = TemplateKind::from_str(template).unwrap(); From 75222d93ca558d079aa577d2b878608d8f2d77a8 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:20:25 +0200 Subject: [PATCH 09/12] ci: add workflows Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- .github/dependabot.yml | 6 + .github/workflows/audit.yml | 31 +++ .github/workflows/breaking_changes.yml | 30 +++ .github/workflows/check-and-lint.yaml | 68 ++++++ .github/workflows/lint-markdown-toml.yaml | 14 ++ .github/workflows/release.yaml | 228 ++++++++++++++++++++ .github/workflows/tests.yaml | 39 ++++ crates/organize-rs_core/src/error.rs | 2 +- crates/organize-rs_core/tests/public_api.rs | 2 +- 9 files changed, 418 insertions(+), 2 deletions(-) create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/audit.yml create mode 100644 .github/workflows/breaking_changes.yml create mode 100644 .github/workflows/check-and-lint.yaml create mode 100644 .github/workflows/lint-markdown-toml.yaml create mode 100644 .github/workflows/release.yaml create mode 100644 .github/workflows/tests.yaml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..1230149 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/.github/workflows/audit.yml b/.github/workflows/audit.yml new file mode 100644 index 0000000..7302fe2 --- /dev/null +++ b/.github/workflows/audit.yml @@ -0,0 +1,31 @@ +name: Security audit + +on: + schedule: + # Runs at 00:00 UTC everyday + - cron: "0 0 * * *" + push: + paths: + - "**/Cargo.toml" + - "**/Cargo.lock" + - "crates/**/Cargo.toml" + - "crates/**/Cargo.lock" + pull_request: + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v3 + # Ensure that the latest version of Cargo is installed + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/breaking_changes.yml b/.github/workflows/breaking_changes.yml new file mode 100644 index 0000000..abbf95b --- /dev/null +++ b/.github/workflows/breaking_changes.yml @@ -0,0 +1,30 @@ +on: + push: + branches: + - release/* + +name: Breaking changes + +jobs: + test: + name: Test + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + rust: [stable] + job: + - os: macos-latest + - os: ubuntu-latest + - os: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/cargo@v1 + with: + command: test + args: -r --all-targets --all-features --workspace -- --ignored diff --git a/.github/workflows/check-and-lint.yaml b/.github/workflows/check-and-lint.yaml new file mode 100644 index 0000000..67ceda6 --- /dev/null +++ b/.github/workflows/check-and-lint.yaml @@ -0,0 +1,68 @@ +on: + pull_request: + branches: + - main + paths-ignore: + - "**/*.md" + - "docs/**/*" + push: + branches: + - main + paths-ignore: + - "**/*.md" + - "docs/**/*" + +name: Check and Lint +# env: +# RUSTFLAGS: "-Dwarnings" + +jobs: + check: + name: Check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/cargo@v1 + with: + command: check + args: --all-features --workspace + + fmt: + name: Rustfmt + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - run: rustup component add rustfmt + - uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + clippy: + name: Clippy + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + components: clippy + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-targets --all-features -- -D warnings + name: Clippy Output diff --git a/.github/workflows/lint-markdown-toml.yaml b/.github/workflows/lint-markdown-toml.yaml new file mode 100644 index 0000000..f8620c5 --- /dev/null +++ b/.github/workflows/lint-markdown-toml.yaml @@ -0,0 +1,14 @@ +name: Lint Markdown / Toml + +on: + push: + branches: [main] + pull_request: + +jobs: + style: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - uses: dprint/check@v2.2 diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml new file mode 100644 index 0000000..670f0cb --- /dev/null +++ b/.github/workflows/release.yaml @@ -0,0 +1,228 @@ +on: + pull_request: + branches: + - main + paths-ignore: + - "**/*.md" + - "docs/**/*" + push: + branches: + - main + - release/* + paths-ignore: + - "**/*.md" + - "docs/**/*" + +name: Build release binaries + +jobs: + publish: + name: Publishing ${{ matrix.job.target }} + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + rust: [stable] + job: + - os: windows-latest + os-name: windows + target: x86_64-pc-windows-msvc + architecture: x86_64 + binary-postfix: ".exe" + use-cross: false + - os: macos-latest + os-name: macos + target: x86_64-apple-darwin + architecture: x86_64 + binary-postfix: "" + use-cross: false + - os: macos-latest + os-name: macos + target: aarch64-apple-darwin + architecture: arm64 + binary-postfix: "" + use-cross: true + - os: ubuntu-latest + os-name: linux + target: x86_64-unknown-linux-gnu + architecture: x86_64 + binary-postfix: "" + use-cross: false + - os: ubuntu-latest + os-name: linux + target: x86_64-unknown-linux-musl + architecture: x86_64 + binary-postfix: "" + use-cross: false + - os: ubuntu-latest + os-name: linux + target: aarch64-unknown-linux-gnu + architecture: arm64 + binary-postfix: "" + use-cross: true + - os: ubuntu-latest + os-name: linux + target: i686-unknown-linux-gnu + architecture: i686 + binary-postfix: "" + use-cross: true + - os: ubuntu-latest + os-name: netbsd + target: x86_64-unknown-netbsd + architecture: x86_64 + binary-postfix: "" + use-cross: true + - os: ubuntu-latest + os-name: linux + target: armv7-unknown-linux-gnueabihf + architecture: armv7 + binary-postfix: "" + use-cross: true + + steps: + - name: Checkout repository + uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Install Rust toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: ${{ matrix.rust }} + profile: minimal + override: true + target: ${{ matrix.job.target }} + - name: install compiler + shell: bash + run: | + if [[ ${{ matrix.job.target }} == x86_64-unknown-linux-musl ]]; then + sudo apt update + sudo apt-get install -y musl-tools + fi + - name: install cargo-auditable for non-cross builds + shell: bash + if: ${{ matrix.job.use_cross != true}} + run: | + cargo install cargo-auditable cargo-audit + - uses: Swatinem/rust-cache@v2 + with: + key: ${{ matrix.job.target }} + - name: Set Version + shell: bash + run: echo "PROJECT_VERSION=$(git describe --tags)" >> $GITHUB_ENV + - name: Cargo build + uses: actions-rs/cargo@v1 + if: ${{ matrix.job.use-cross == true }} + with: + command: build + use-cross: ${{ matrix.job.use-cross }} + toolchain: ${{ matrix.rust }} + args: --release --target ${{ matrix.job.target }} + - name: Cargo auditable build + uses: actions-rs/cargo@v1 + if: ${{ matrix.job.use-cross == false }} + with: + command: auditable + use-cross: ${{ matrix.job.use-cross }} + toolchain: ${{ matrix.rust }} + args: build --release --target ${{ matrix.job.target }} + - name: Packaging final binary + shell: bash + id: package-binary + run: | + cp -a config target/${{ matrix.job.target }}/release/config + cd target/${{ matrix.job.target }}/release + + ########## create tar.gz ########## + + # accounting for main branch and therefore beta builds + if [[ ${{ github.ref }} = refs/heads/main ]]; then + RELEASE_NAME=organize-beta-${{ matrix.job.target}}.tar.gz + elif [[ ${{ github.ref }} = refs/tags/* ]]; then + RELEASE_NAME=organize-${{ github.ref_name }}-${{ matrix.job.target}}.tar.gz + else + RELEASE_NAME=organize-${{ github.run_id }}-${{ github.run_attempt }}-${{ matrix.job.target}}.tar.gz + fi + + tar czvf $RELEASE_NAME organize${{ matrix.job.binary-postfix }} config/ + + ########## create sha256 ########## + if [[ ${{ runner.os }} == 'Windows' ]]; then + certutil -hashfile $RELEASE_NAME sha256 | grep -E [A-Fa-f0-9]{64} > $RELEASE_NAME.sha256 + else + shasum -a 256 $RELEASE_NAME > $RELEASE_NAME.sha256 + fi + + echo "release_name=$RELEASE_NAME" >> $GITHUB_OUTPUT + - name: Storing binary as artefact + uses: actions/upload-artifact@v3 + with: + name: binary-${{ matrix.job.target}} + path: target/${{ matrix.job.target }}/release/${{ steps.package-binary.outputs.release_name }}* + - name: Releasing release versions + uses: softprops/action-gh-release@v1 + if: startsWith(github.ref, 'refs/tags/') + with: + files: | + target/${{ matrix.job.target }}/release/${{ steps.package-binary.outputs.release_name }}* + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + # publish-beta: + # name: Publishing beta-builds + # needs: publish + # if: ${{ github.ref == 'refs/heads/main' }} + # runs-on: ubuntu-latest + # steps: + # - name: Download all workflow run artifacts + # uses: actions/download-artifact@v3 + # - name: Releasing beta builds + # shell: bash + # run: | + # # set up some directories + # WORKING_DIR=$(mktemp -d) + # DEST_DIR=beta + + # # set up the github deploy key + # mkdir -p ~/.ssh + # echo "${{ secrets.BETA_RELEASE_KEY }}" > ~/.ssh/id_rsa + # chmod 600 ~/.ssh/id_rsa + + # # set up git + # git config --global user.name "${{ github.actor }}" + # git config --global user.email "${{ github.actor }}" + # ssh-keyscan -H github.com > ~/.ssh/known_hosts + # GIT_SSH='ssh -i ~/.ssh/id_rsa -o UserKnownHostsFile=~/.ssh/known_hosts' + + # # clone the repo into our working directory + # GIT_SSH_COMMAND=$GIT_SSH git clone git@github.com:organize-rs/organize-beta.git $WORKING_DIR + + # # ensure destination directory exists + # mkdir -p $WORKING_DIR/$DEST_DIR + + # # do the copy + # for i in binary-*; do cp -a $i/* $WORKING_DIR/$DEST_DIR; done + + # # create the commit + # cd $WORKING_DIR + # git add . + # git commit -m "${{ github.job }} from https://github.com/${{ github.repository }}/commit/${{ github.sha }}" || echo + # GIT_SSH_COMMAND=$GIT_SSH git pull --rebase + # GIT_SSH_COMMAND=$GIT_SSH git push + + # # publish-cargo: + # # name: Publishing to Cargo + # # runs-on: ubuntu-latest + # # steps: + # # - name: Checkout repository + # # uses: actions/checkout@v3 + # # - uses: actions-rs/toolchain@v1 + # # with: + # # toolchain: stable + # # profile: minimal + # # override: true + # # - uses: Swatinem/rust-cache@v2 + # # with: + # # key: ${{ matrix.job.target }} + # # - uses: actions-rs/cargo@v1 + # # with: + # # command: publish + # # args: --token ${{ secrets.CARGO_API_KEY }} diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 0000000..a342150 --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,39 @@ +on: + pull_request: + branches: + - main + paths-ignore: + - "**/*.md" + - "docs/**/*" + push: + branches: + - main + paths-ignore: + - "**/*.md" + - "docs/**/*" + +name: Tests + +jobs: + test: + name: Test + runs-on: ${{ matrix.job.os }} + strategy: + matrix: + rust: [stable] + job: + - os: macos-latest + - os: ubuntu-latest + - os: windows-latest + steps: + - uses: actions/checkout@v3 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: stable + override: true + - uses: Swatinem/rust-cache@v2 + - uses: actions-rs/cargo@v1 + with: + command: test + args: -r --all-targets --all-features --workspace diff --git a/crates/organize-rs_core/src/error.rs b/crates/organize-rs_core/src/error.rs index 331a770..dd14ac9 100644 --- a/crates/organize-rs_core/src/error.rs +++ b/crates/organize-rs_core/src/error.rs @@ -4,7 +4,7 @@ use displaydoc::Display; use std::{error::Error as StdError, path::PathBuf}; use thiserror::Error as ThisError; -/// Result type often returned from methods that can have rustic `Error`s. +/// Result type often returned from methods that can have organize `Error`s. pub type OrganizeResult = std::result::Result; // [`Error`] is public, but opaque and easy to keep compatible. diff --git a/crates/organize-rs_core/tests/public_api.rs b/crates/organize-rs_core/tests/public_api.rs index 7224185..95183ed 100644 --- a/crates/organize-rs_core/tests/public_api.rs +++ b/crates/organize-rs_core/tests/public_api.rs @@ -1,4 +1,4 @@ -//! Rustic_Core Integration Test for the Public API of the library +//! organize_core Integration Test for the Public API of the library //! //! Installs the nightly toolchain, produces documentation and derives //! the public API from the rustdoc JSON. Then compares it with our From 4f4160004896cf96e5e54b23b4d7755404847186 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:03:38 +0200 Subject: [PATCH 10/12] Add renovate.json (#9) Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> --- renovate.json | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 renovate.json diff --git a/renovate.json b/renovate.json new file mode 100644 index 0000000..39a2b6e --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] +} From fee9d5d634bca0090456f8c65dc3fd1252fc8073 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:24:10 +0200 Subject: [PATCH 11/12] style: dprint fmt Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- .vscode/launch.json | 4 +- CHANGELOG.md | 487 ++++++++++++------- Cargo.toml | 82 ++-- README.md | 73 ++- TODO.md | 163 ++++--- crates/organize-rs_core/CHANGELOG.md | 670 +++++++++++++++++--------- crates/organize-rs_core/Cargo.toml | 65 ++- crates/organize-rs_testing/Cargo.toml | 4 +- docs/DSL.md | 30 +- dprint.json | 30 ++ renovate.json | 8 +- xtask/Cargo.toml | 2 +- 12 files changed, 1017 insertions(+), 601 deletions(-) create mode 100644 dprint.json diff --git a/.vscode/launch.json b/.vscode/launch.json index a1d8d8e..46b7fcd 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -229,7 +229,7 @@ "run", "config", "--paths", - "config\\empty.yaml", + "config\\empty.yaml" ], "cwd": "${workspaceFolder}" }, @@ -272,4 +272,4 @@ "cwd": "${workspaceFolder}" } ] -} \ No newline at end of file +} diff --git a/CHANGELOG.md b/CHANGELOG.md index 304b22e..808dbcb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,34 +3,43 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and this project adheres to +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.4.1 (2023-06-05) ### New Features - - Reuse existing commands filter/action to output config snippets - - Implement confirmation dialogue before irreversibly applying an action - - implement previewing an action - - split the pipeline into several actors that could be run in parallel if needed +- Reuse existing commands + filter/action to output config snippets +- Implement confirmation + dialogue before irreversibly applying an action +- implement previewing an + action +- split the pipeline into + several actors that could be run in parallel if needed ### Bug Fixes - - Fix descriptions for commands - - Reexport TemplateStringKind for documentation +- Fix descriptions for + commands +- Reexport TemplateStringKind + for documentation ### Other - - add doctests for some actions +- add doctests for some + actions ### Commit Statistics - - 7 commits contributed to the release over the course of 1 calendar day. - - 2 days passed between releases. - - 7 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 7 commits contributed to the release over the course of 1 calendar day. +- 2 days passed between releases. +- 7 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -38,14 +47,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Fix descriptions for commands ([`404d0b8`](https://github.com/organize-rs/organize/commit/404d0b8af5c2b16b454fdcc3b3bcfdef7c709356)) - - Reuse existing commands filter/action to output config snippets ([`ea7d264`](https://github.com/organize-rs/organize/commit/ea7d2648b7adc1fb4cef184f2a4b1c9b2868c46d)) - - Implement confirmation dialogue before irreversibly applying an action ([`975995c`](https://github.com/organize-rs/organize/commit/975995c36897e3c9e68c99daf065cc4c52f8b71c)) - - Implement previewing an action ([`37aad75`](https://github.com/organize-rs/organize/commit/37aad75d242f73091b3fc1058db28c40bbe6da70)) - - Split the pipeline into several actors that could be run in parallel if needed ([`27f0ad6`](https://github.com/organize-rs/organize/commit/27f0ad6f0613eee61df03bf8f2385cee94799f85)) - - Reexport TemplateStringKind for documentation ([`859f50f`](https://github.com/organize-rs/organize/commit/859f50fdfab805a51567808f0631a24a3b522298)) - - Add doctests for some actions ([`7144ddd`](https://github.com/organize-rs/organize/commit/7144ddda26a7a4368d5c5af3a3108df8217cee4f)) +- **Uncategorized** + - Fix descriptions for commands + ([`404d0b8`](https://github.com/organize-rs/organize/commit/404d0b8af5c2b16b454fdcc3b3bcfdef7c709356)) + - Reuse existing commands filter/action to output config snippets + ([`ea7d264`](https://github.com/organize-rs/organize/commit/ea7d2648b7adc1fb4cef184f2a4b1c9b2868c46d)) + - Implement confirmation dialogue before irreversibly applying an action + ([`975995c`](https://github.com/organize-rs/organize/commit/975995c36897e3c9e68c99daf065cc4c52f8b71c)) + - Implement previewing an action + ([`37aad75`](https://github.com/organize-rs/organize/commit/37aad75d242f73091b3fc1058db28c40bbe6da70)) + - Split the pipeline into several actors that could be run in parallel if + needed + ([`27f0ad6`](https://github.com/organize-rs/organize/commit/27f0ad6f0613eee61df03bf8f2385cee94799f85)) + - Reexport TemplateStringKind for documentation + ([`859f50f`](https://github.com/organize-rs/organize/commit/859f50fdfab805a51567808f0631a24a3b522298)) + - Add doctests for some actions + ([`7144ddd`](https://github.com/organize-rs/organize/commit/7144ddda26a7a4368d5c5af3a3108df8217cee4f)) +
## 0.4.0 (2023-06-03) @@ -74,7 +92,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - @@ -100,62 +117,87 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - fully annotate template config - - cargo fix & cargo fmt +- fully annotate template + config +- cargo fix & cargo fmt ### Documentation - - start annotating a template config file +- start annotating a template + config file ### New Features - - implement parsing rules from config files - - docs command opens browser - - adding cli option for filter mode - - Free the way for applying multiple filters +- implement parsing rules + from config files +- docs command opens browser +- adding cli option for + filter mode +- Free the way for applying + multiple filters ### Bug Fixes - - Fix generate completions +- Fix generate completions ### Other - - check for tags when running rules from config file - - implement template generation - - implement some snapshot testing - - fix naming in config - - more state changes for runner - - implement first part of rule runner - - first implementation draft for a config run - - parse first parts of py-organize config - - Switch to jwalk to walk the directory tree parallelized - - add new run command to run a specified rule from a config or script - - add new config command to generate and check configs - - apply multiple filters to walkdir iter +- check for tags when running + rules from config file +- implement template + generation +- implement some snapshot + testing +- fix naming in config +- more state changes for + runner +- implement first part of + rule runner +- first implementation draft + for a config run +- parse first parts of + py-organize config +- Switch to jwalk to walk the + directory tree parallelized +- add new run command to run + a specified rule from a config or script +- add new config command to + generate and check configs +- apply multiple filters to + walkdir iter ### Refactor - - split filters in seperate modules - - flatten library structure - - change Rule to contain new types - - make command arguments more reasonable - - add scripting and shell filter, add check for config and scripts subcommand, and run subcommands for config and scripts, add generate subcommand - - improve display - - Make FilterWalker more configurable for upcoming features - - make filters use more option/result methods +- split filters in seperate + modules +- flatten library structure +- change Rule to contain new + types +- make command arguments more + reasonable +- add scripting and shell + filter, add check for config and scripts subcommand, and run subcommands for + config and scripts, add generate subcommand +- improve display +- Make FilterWalker more + configurable for upcoming features +- make filters use more + option/result methods ### Other (BREAKING) - - Re-export filters and actions from core library for documentation purposes +- Re-export filters and + actions from core library for documentation purposes ### Commit Statistics - - 32 commits contributed to the release over the course of 9 calendar days. - - 9 days passed between releases. - - 29 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 32 commits contributed to the release over the course of 9 calendar days. +- 9 days passed between releases. +- 29 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -163,39 +205,73 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Re-export filters and actions from core library for documentation purposes ([`1398491`](https://github.com/organize-rs/organize/commit/13984918e9cbbe3a7787af715004b5729b5a58a8)) - - Fully annotate template config ([`930593f`](https://github.com/organize-rs/organize/commit/930593f55f0128609daf98ccee901d5e33c29223)) - - Check for tags when running rules from config file ([`7c0b0c9`](https://github.com/organize-rs/organize/commit/7c0b0c9ce915e8b43d4542bc79b53432dd5bc2d2)) - - Cargo fix & cargo fmt ([`cb1e115`](https://github.com/organize-rs/organize/commit/cb1e1151c09afed48f40b989132ae89324f9f2b1)) - - Implement template generation ([`19110b9`](https://github.com/organize-rs/organize/commit/19110b9592fb35314402f9bac71f52791c5a7cd1)) - - Start annotating a template config file ([`0f49e89`](https://github.com/organize-rs/organize/commit/0f49e89282eaa8fc5f11a1db81553484e9a669b9)) - - Implement some snapshot testing ([`ad6f684`](https://github.com/organize-rs/organize/commit/ad6f6844dc93dac07e4d1921d1dbe0ab984b15c9)) - - Implement parsing rules from config files ([`94757b5`](https://github.com/organize-rs/organize/commit/94757b5e2eddb54b0a706634f408bb5fcabfaf94)) - - Fix naming in config ([`22192eb`](https://github.com/organize-rs/organize/commit/22192eb528c2f95ad01ef124082dfae518e8c464)) - - More state changes for runner ([`afb116b`](https://github.com/organize-rs/organize/commit/afb116bc91add3ac76f6c55e44932391eec6e7e8)) - - Implement first part of rule runner ([`3f6df32`](https://github.com/organize-rs/organize/commit/3f6df32eac07d369b035ffb06156c2b47b0f9121)) - - First implementation draft for a config run ([`abdc5ec`](https://github.com/organize-rs/organize/commit/abdc5ecfe89c8db0b3f7077681ef3a23a7797df5)) - - Split filters in seperate modules ([`85fd452`](https://github.com/organize-rs/organize/commit/85fd452e00835f14e84da9547bb5659449771cde)) - - More deserialization attempts ([`c32b4b3`](https://github.com/organize-rs/organize/commit/c32b4b30c602b61d5ce94a037ccf2a496232c935)) - - Flatten library structure ([`8b47644`](https://github.com/organize-rs/organize/commit/8b47644af39c99f79658cdd54c067f9787b02347)) - - Parse first parts of py-organize config ([`91a8840`](https://github.com/organize-rs/organize/commit/91a8840c5826eb39493384a3e2d1bbb69f38c298)) - - Change Rule to contain new types ([`2c06e62`](https://github.com/organize-rs/organize/commit/2c06e62ede4f1de31c75dd786c65f87b0855dffc)) - - Fix generate completions ([`435488c`](https://github.com/organize-rs/organize/commit/435488c65e552016a8c6a9f6849390441db2e1e6)) - - Make command arguments more reasonable ([`fb1a215`](https://github.com/organize-rs/organize/commit/fb1a2158884b70ea37df7b332acd6d4170b07e91)) - - Add scripting and shell filter, add check for config and scripts subcommand, and run subcommands for config and scripts, add generate subcommand ([`54a8993`](https://github.com/organize-rs/organize/commit/54a8993981273a39fb0da07de5853a2fbc5764b4)) - - Start sketching out scripting ideas ([`54a3446`](https://github.com/organize-rs/organize/commit/54a34468662642d3e2f161425be28cb957859b78)) - - Switch to jwalk to walk the directory tree parallelized ([`18ee702`](https://github.com/organize-rs/organize/commit/18ee7029fa931b0f95046f11c2919b23f11c1470)) - - Add new run command to run a specified rule from a config or script ([`075f025`](https://github.com/organize-rs/organize/commit/075f0256f0dc5c2a69e350c2232468988f31c3e2)) - - Add new config command to generate and check configs ([`b544485`](https://github.com/organize-rs/organize/commit/b544485a914108185f2e10e1b7ae336acdc7ac5e)) - - Docs command opens browser ([`1338889`](https://github.com/organize-rs/organize/commit/13388896c02874263ff7838591fd4776a7912b1e)) - - Improve display ([`99d7b0c`](https://github.com/organize-rs/organize/commit/99d7b0c109017a78c1ef726dcc9d40d0507b0426)) - - Adding cli option for filter mode ([`d088c50`](https://github.com/organize-rs/organize/commit/d088c50c894a9a94a47b2e3b66502fa3d5fdfc59)) - - Make FilterWalker more configurable for upcoming features ([`a82a208`](https://github.com/organize-rs/organize/commit/a82a20821ebe60d386efcb9856f4d22b45ab4a2e)) - - Free the way for applying multiple filters ([`7c29918`](https://github.com/organize-rs/organize/commit/7c2991827f503c7caf6f82e964f9cc91f5c39d0e)) - - Fix borrow issues ([`398edb2`](https://github.com/organize-rs/organize/commit/398edb29916398c43b15531779233eda4b28eef5)) - - Apply multiple filters to walkdir iter ([`a316302`](https://github.com/organize-rs/organize/commit/a3163027800f1843476b8855a41f2115fdb3d3eb)) - - Make filters use more option/result methods ([`fd90047`](https://github.com/organize-rs/organize/commit/fd90047424eb9e6f04481a1ef35825e360b06912)) +- **Uncategorized** + - Re-export filters and actions from core library for documentation purposes + ([`1398491`](https://github.com/organize-rs/organize/commit/13984918e9cbbe3a7787af715004b5729b5a58a8)) + - Fully annotate template config + ([`930593f`](https://github.com/organize-rs/organize/commit/930593f55f0128609daf98ccee901d5e33c29223)) + - Check for tags when running rules from config file + ([`7c0b0c9`](https://github.com/organize-rs/organize/commit/7c0b0c9ce915e8b43d4542bc79b53432dd5bc2d2)) + - Cargo fix & cargo fmt + ([`cb1e115`](https://github.com/organize-rs/organize/commit/cb1e1151c09afed48f40b989132ae89324f9f2b1)) + - Implement template generation + ([`19110b9`](https://github.com/organize-rs/organize/commit/19110b9592fb35314402f9bac71f52791c5a7cd1)) + - Start annotating a template config file + ([`0f49e89`](https://github.com/organize-rs/organize/commit/0f49e89282eaa8fc5f11a1db81553484e9a669b9)) + - Implement some snapshot testing + ([`ad6f684`](https://github.com/organize-rs/organize/commit/ad6f6844dc93dac07e4d1921d1dbe0ab984b15c9)) + - Implement parsing rules from config files + ([`94757b5`](https://github.com/organize-rs/organize/commit/94757b5e2eddb54b0a706634f408bb5fcabfaf94)) + - Fix naming in config + ([`22192eb`](https://github.com/organize-rs/organize/commit/22192eb528c2f95ad01ef124082dfae518e8c464)) + - More state changes for runner + ([`afb116b`](https://github.com/organize-rs/organize/commit/afb116bc91add3ac76f6c55e44932391eec6e7e8)) + - Implement first part of rule runner + ([`3f6df32`](https://github.com/organize-rs/organize/commit/3f6df32eac07d369b035ffb06156c2b47b0f9121)) + - First implementation draft for a config run + ([`abdc5ec`](https://github.com/organize-rs/organize/commit/abdc5ecfe89c8db0b3f7077681ef3a23a7797df5)) + - Split filters in seperate modules + ([`85fd452`](https://github.com/organize-rs/organize/commit/85fd452e00835f14e84da9547bb5659449771cde)) + - More deserialization attempts + ([`c32b4b3`](https://github.com/organize-rs/organize/commit/c32b4b30c602b61d5ce94a037ccf2a496232c935)) + - Flatten library structure + ([`8b47644`](https://github.com/organize-rs/organize/commit/8b47644af39c99f79658cdd54c067f9787b02347)) + - Parse first parts of py-organize config + ([`91a8840`](https://github.com/organize-rs/organize/commit/91a8840c5826eb39493384a3e2d1bbb69f38c298)) + - Change Rule to contain new types + ([`2c06e62`](https://github.com/organize-rs/organize/commit/2c06e62ede4f1de31c75dd786c65f87b0855dffc)) + - Fix generate completions + ([`435488c`](https://github.com/organize-rs/organize/commit/435488c65e552016a8c6a9f6849390441db2e1e6)) + - Make command arguments more reasonable + ([`fb1a215`](https://github.com/organize-rs/organize/commit/fb1a2158884b70ea37df7b332acd6d4170b07e91)) + - Add scripting and shell filter, add check for config and scripts subcommand, + and run subcommands for config and scripts, add generate subcommand + ([`54a8993`](https://github.com/organize-rs/organize/commit/54a8993981273a39fb0da07de5853a2fbc5764b4)) + - Start sketching out scripting ideas + ([`54a3446`](https://github.com/organize-rs/organize/commit/54a34468662642d3e2f161425be28cb957859b78)) + - Switch to jwalk to walk the directory tree parallelized + ([`18ee702`](https://github.com/organize-rs/organize/commit/18ee7029fa931b0f95046f11c2919b23f11c1470)) + - Add new run command to run a specified rule from a config or script + ([`075f025`](https://github.com/organize-rs/organize/commit/075f0256f0dc5c2a69e350c2232468988f31c3e2)) + - Add new config command to generate and check configs + ([`b544485`](https://github.com/organize-rs/organize/commit/b544485a914108185f2e10e1b7ae336acdc7ac5e)) + - Docs command opens browser + ([`1338889`](https://github.com/organize-rs/organize/commit/13388896c02874263ff7838591fd4776a7912b1e)) + - Improve display + ([`99d7b0c`](https://github.com/organize-rs/organize/commit/99d7b0c109017a78c1ef726dcc9d40d0507b0426)) + - Adding cli option for filter mode + ([`d088c50`](https://github.com/organize-rs/organize/commit/d088c50c894a9a94a47b2e3b66502fa3d5fdfc59)) + - Make FilterWalker more configurable for upcoming features + ([`a82a208`](https://github.com/organize-rs/organize/commit/a82a20821ebe60d386efcb9856f4d22b45ab4a2e)) + - Free the way for applying multiple filters + ([`7c29918`](https://github.com/organize-rs/organize/commit/7c2991827f503c7caf6f82e964f9cc91f5c39d0e)) + - Fix borrow issues + ([`398edb2`](https://github.com/organize-rs/organize/commit/398edb29916398c43b15531779233eda4b28eef5)) + - Apply multiple filters to walkdir iter + ([`a316302`](https://github.com/organize-rs/organize/commit/a3163027800f1843476b8855a41f2115fdb3d3eb)) + - Make filters use more option/result methods + ([`fd90047`](https://github.com/organize-rs/organize/commit/fd90047424eb9e6f04481a1ef35825e360b06912)) +
## 0.3.1 (2023-05-25) @@ -208,7 +284,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - @@ -218,31 +293,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New Features - - docs command opens browser - - adding cli option for filter mode - - Free the way for applying multiple filters +- docs command opens browser +- adding cli option for + filter mode +- Free the way for applying + multiple filters ### Other - - Switch to jwalk to walk the directory tree parallelized - - add new run command to run a specified rule from a config or script - - add new config command to generate and check configs - - apply multiple filters to walkdir iter +- Switch to jwalk to walk the + directory tree parallelized +- add new run command to run + a specified rule from a config or script +- add new config command to + generate and check configs +- apply multiple filters to + walkdir iter ### Refactor - - improve display - - Make FilterWalker more configurable for upcoming features - - make filters use more option/result methods +- improve display +- Make FilterWalker more + configurable for upcoming features +- make filters use more + option/result methods ### Commit Statistics - - 12 commits contributed to the release over the course of 3 calendar days. - - 3 days passed between releases. - - 10 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 12 commits contributed to the release over the course of 3 calendar days. +- 3 days passed between releases. +- 10 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -250,44 +334,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Switch to jwalk to walk the directory tree parallelized ([`4757013`](https://github.com/organize-rs/organize/commit/4757013ebe9fe5d37ea3d7b7cddf155910e0f5b4)) - - Add new run command to run a specified rule from a config or script ([`95b9b3c`](https://github.com/organize-rs/organize/commit/95b9b3cf0ce06a224c0c79782ae7c470c31475d8)) - - Add new config command to generate and check configs ([`36d02df`](https://github.com/organize-rs/organize/commit/36d02df0f52798af534151ea4d3ed4f7876934b2)) - - Docs command opens browser ([`1246468`](https://github.com/organize-rs/organize/commit/1246468f87132a8b52169fd186f3626f357817bd)) - - Improve display ([`543150d`](https://github.com/organize-rs/organize/commit/543150dd96aca886bacd0057bda1957d19b4322d)) - - Adding cli option for filter mode ([`fea4cc1`](https://github.com/organize-rs/organize/commit/fea4cc14fe1f64cc9fd91664bf07f8940cba15a1)) - - Make FilterWalker more configurable for upcoming features ([`14300ea`](https://github.com/organize-rs/organize/commit/14300ea60bcccf500d813ee267792899a278a9ff)) - - Free the way for applying multiple filters ([`33fc910`](https://github.com/organize-rs/organize/commit/33fc910001ac35967bc1e424b110c88ace6b9186)) - - Fix borrow issues ([`3d1b0a1`](https://github.com/organize-rs/organize/commit/3d1b0a19e71441bdec6a9b609833b91a8ef890d8)) - - Apply multiple filters to walkdir iter ([`09c428c`](https://github.com/organize-rs/organize/commit/09c428cc45bbb348ca08e5fd233c999408ca2500)) - - Make filters use more option/result methods ([`c005013`](https://github.com/organize-rs/organize/commit/c005013d1c49f5d717d635c3ece760bb5c904e09)) - - Start sketching out scripting ideas ([`b84d301`](https://github.com/organize-rs/organize/commit/b84d3019f5d334c61bb85733db16bf85ed7072d7)) +- **Uncategorized** + - Switch to jwalk to walk the directory tree parallelized + ([`4757013`](https://github.com/organize-rs/organize/commit/4757013ebe9fe5d37ea3d7b7cddf155910e0f5b4)) + - Add new run command to run a specified rule from a config or script + ([`95b9b3c`](https://github.com/organize-rs/organize/commit/95b9b3cf0ce06a224c0c79782ae7c470c31475d8)) + - Add new config command to generate and check configs + ([`36d02df`](https://github.com/organize-rs/organize/commit/36d02df0f52798af534151ea4d3ed4f7876934b2)) + - Docs command opens browser + ([`1246468`](https://github.com/organize-rs/organize/commit/1246468f87132a8b52169fd186f3626f357817bd)) + - Improve display + ([`543150d`](https://github.com/organize-rs/organize/commit/543150dd96aca886bacd0057bda1957d19b4322d)) + - Adding cli option for filter mode + ([`fea4cc1`](https://github.com/organize-rs/organize/commit/fea4cc14fe1f64cc9fd91664bf07f8940cba15a1)) + - Make FilterWalker more configurable for upcoming features + ([`14300ea`](https://github.com/organize-rs/organize/commit/14300ea60bcccf500d813ee267792899a278a9ff)) + - Free the way for applying multiple filters + ([`33fc910`](https://github.com/organize-rs/organize/commit/33fc910001ac35967bc1e424b110c88ace6b9186)) + - Fix borrow issues + ([`3d1b0a1`](https://github.com/organize-rs/organize/commit/3d1b0a19e71441bdec6a9b609833b91a8ef890d8)) + - Apply multiple filters to walkdir iter + ([`09c428c`](https://github.com/organize-rs/organize/commit/09c428cc45bbb348ca08e5fd233c999408ca2500)) + - Make filters use more option/result methods + ([`c005013`](https://github.com/organize-rs/organize/commit/c005013d1c49f5d717d635c3ece760bb5c904e09)) + - Start sketching out scripting ideas + ([`b84d301`](https://github.com/organize-rs/organize/commit/b84d3019f5d334c61bb85733db16bf85ed7072d7)) +
## 0.3.0 (2023-05-21) ### Feature -- Breaking change: Change syntax for date based filters to use range based syntax +- Breaking change: Change syntax for date based filters to use range based + syntax ## 0.2.6 (2023-05-21) - - ### Chore - - add description to lib.rs +- add description to lib.rs ### Commit Statistics - - 1 commit contributed to the release. - - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 1 commit contributed to the release. +- 1 commit was understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -295,24 +392,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Add description to lib.rs ([`d2b3328`](https://github.com/organize-rs/organize/commit/d2b33280185f68daaff2671d1a4470d9041f3bcb)) +- **Uncategorized** + - Add description to lib.rs + ([`d2b3328`](https://github.com/organize-rs/organize/commit/d2b33280185f68daaff2671d1a4470d9041f3bcb)) +
## 0.2.5 (2023-05-20) ### New Features - - Implement `mimetype` filter +- Implement `mimetype` filter ### Commit Statistics - - 8 commits contributed to the release over the course of 1 calendar day. - - 1 day passed between releases. - - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 8 commits contributed to the release over the course of 1 calendar day. +- 1 day passed between releases. +- 1 commit was understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -320,15 +420,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Implement `mimetype` filter ([`345d888`](https://github.com/organize-rs/organize/commit/345d8885d1ffe9bcfdc42c62fccbdc59a457ed0a)) - - Implement `created` filter ([`f07ab6a`](https://github.com/organize-rs/organize/commit/f07ab6a4bd9be7674dad416f7b74e9b9196b3dca)) - - Remove human-panic dependency ([`9382256`](https://github.com/organize-rs/organize/commit/938225668c8879192a8e81a4872797907e3b4641)) - - Research dependencies ([`9f12de9`](https://github.com/organize-rs/organize/commit/9f12de940ba56278c3eec43449dd5663f284e1e4)) - - Cargo fix & cargo fmt ([`ee231a6`](https://github.com/organize-rs/organize/commit/ee231a69fcd825e6121c380f408c21ff2bf6c425)) - - Cargo fix ([`0695061`](https://github.com/organize-rs/organize/commit/06950617d566bd19764a3f4b403a92f787b2536d)) - - Add doc comments for ignore args ([`626a2ac`](https://github.com/organize-rs/organize/commit/626a2ac78814a6fb4f654f22bb27e422aa136fcf)) - - Implement `empty` filter and global ignore for file names and directory paths ([`d51a81a`](https://github.com/organize-rs/organize/commit/d51a81a593cb37c54c0c91edfac60a5eb8de7c89)) +- **Uncategorized** + - Implement `mimetype` filter + ([`345d888`](https://github.com/organize-rs/organize/commit/345d8885d1ffe9bcfdc42c62fccbdc59a457ed0a)) + - Implement `created` filter + ([`f07ab6a`](https://github.com/organize-rs/organize/commit/f07ab6a4bd9be7674dad416f7b74e9b9196b3dca)) + - Remove human-panic dependency + ([`9382256`](https://github.com/organize-rs/organize/commit/938225668c8879192a8e81a4872797907e3b4641)) + - Research dependencies + ([`9f12de9`](https://github.com/organize-rs/organize/commit/9f12de940ba56278c3eec43449dd5663f284e1e4)) + - Cargo fix & cargo fmt + ([`ee231a6`](https://github.com/organize-rs/organize/commit/ee231a69fcd825e6121c380f408c21ff2bf6c425)) + - Cargo fix + ([`0695061`](https://github.com/organize-rs/organize/commit/06950617d566bd19764a3f4b403a92f787b2536d)) + - Add doc comments for ignore args + ([`626a2ac`](https://github.com/organize-rs/organize/commit/626a2ac78814a6fb4f654f22bb27e422aa136fcf)) + - Implement `empty` filter and global ignore for file names and directory + paths + ([`d51a81a`](https://github.com/organize-rs/organize/commit/d51a81a593cb37c54c0c91edfac60a5eb8de7c89)) +
## 0.2.4 (2023-05-20) @@ -345,10 +455,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 13 commits contributed to the release over the course of 3 calendar days. - - 3 days passed between releases. - - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 13 commits contributed to the release over the course of 3 calendar days. +- 3 days passed between releases. +- 0 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -356,20 +467,34 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Fix some grouping issues in Cli, create `cli` feature in organize-rs_core ([`b734e62`](https://github.com/organize-rs/organize/commit/b734e625d869163b07f63923414ffa900f93ca64)) - - Implement `filter_by_extension` ([`45e4d5b`](https://github.com/organize-rs/organize/commit/45e4d5b03185d5cd016d16795fdba0336c1496bd)) - - First try for implementing a file extension filter ([`45f2966`](https://github.com/organize-rs/organize/commit/45f296647ea46461ec89550f48eb22e07c037d5c)) - - Fix indirection ([`e6fde80`](https://github.com/organize-rs/organize/commit/e6fde8017240234eb4cb7e1adb259b5a2b6abd7c)) - - Implement stub for filter methods ([`6c6f0f8`](https://github.com/organize-rs/organize/commit/6c6f0f89709a5f7b78ad8de3099ac3cbd6c5f6e3)) - - Add czkawka_core dependency ([`5063aec`](https://github.com/organize-rs/organize/commit/5063aecdd41b99534d7c2539bcd60a5756401110)) - - Refine commands/subcommands ([`ed535f6`](https://github.com/organize-rs/organize/commit/ed535f68f92e4ec187a73fb628fcf4e86d1bda3e)) - - Add `actions` and `filters` as subcommands ([`60df64e`](https://github.com/organize-rs/organize/commit/60df64e3380870fb5182e9cd4f47bb792bc55ce7)) - - Start parsing config ([`0e36272`](https://github.com/organize-rs/organize/commit/0e36272f9e7db8e65daaad39d228d986ab952673)) - - Refactor to workspace and create new core library ([`0de540b`](https://github.com/organize-rs/organize/commit/0de540b0aa0ab07dc4f3b118e6f95b30312ea44e)) - - Support opening text editor on Linux and OSX ([`b5a62b6`](https://github.com/organize-rs/organize/commit/b5a62b611987c1933c8dbfaaaf17a56586d0676e)) - - Implement `edit` command ([`a03feb2`](https://github.com/organize-rs/organize/commit/a03feb276f3f832254c1fc1ce00802ba0b2693cd)) - - Add boilerplate implementations for Enums ([`3c54381`](https://github.com/organize-rs/organize/commit/3c54381db155151046a81b796763fe1bf6bdeefb)) +- **Uncategorized** + - Fix some grouping issues in Cli, create `cli` feature in organize-rs_core + ([`b734e62`](https://github.com/organize-rs/organize/commit/b734e625d869163b07f63923414ffa900f93ca64)) + - Implement `filter_by_extension` + ([`45e4d5b`](https://github.com/organize-rs/organize/commit/45e4d5b03185d5cd016d16795fdba0336c1496bd)) + - First try for implementing a file extension filter + ([`45f2966`](https://github.com/organize-rs/organize/commit/45f296647ea46461ec89550f48eb22e07c037d5c)) + - Fix indirection + ([`e6fde80`](https://github.com/organize-rs/organize/commit/e6fde8017240234eb4cb7e1adb259b5a2b6abd7c)) + - Implement stub for filter methods + ([`6c6f0f8`](https://github.com/organize-rs/organize/commit/6c6f0f89709a5f7b78ad8de3099ac3cbd6c5f6e3)) + - Add czkawka_core dependency + ([`5063aec`](https://github.com/organize-rs/organize/commit/5063aecdd41b99534d7c2539bcd60a5756401110)) + - Refine commands/subcommands + ([`ed535f6`](https://github.com/organize-rs/organize/commit/ed535f68f92e4ec187a73fb628fcf4e86d1bda3e)) + - Add `actions` and `filters` as subcommands + ([`60df64e`](https://github.com/organize-rs/organize/commit/60df64e3380870fb5182e9cd4f47bb792bc55ce7)) + - Start parsing config + ([`0e36272`](https://github.com/organize-rs/organize/commit/0e36272f9e7db8e65daaad39d228d986ab952673)) + - Refactor to workspace and create new core library + ([`0de540b`](https://github.com/organize-rs/organize/commit/0de540b0aa0ab07dc4f3b118e6f95b30312ea44e)) + - Support opening text editor on Linux and OSX + ([`b5a62b6`](https://github.com/organize-rs/organize/commit/b5a62b611987c1933c8dbfaaaf17a56586d0676e)) + - Implement `edit` command + ([`a03feb2`](https://github.com/organize-rs/organize/commit/a03feb276f3f832254c1fc1ce00802ba0b2693cd)) + - Add boilerplate implementations for Enums + ([`3c54381`](https://github.com/organize-rs/organize/commit/3c54381db155151046a81b796763fe1bf6bdeefb)) +
## v0.1.0 (2023-05-15) @@ -378,9 +503,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - - 13 commits contributed to the release. - - 0 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 13 commits contributed to the release. +- 0 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -388,19 +514,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Update Readme and add package details to Cargo.toml ([`7265563`](https://github.com/organize-rs/organize/commit/72655635badb43ad473f82348ee0375e15f312d9)) - - Derive Deserialize and Serialize via Serde ([`2fb46a5`](https://github.com/organize-rs/organize/commit/2fb46a595bf4cc4ebdb1e9a55ae88a036f45c8d3)) - - Derive Copy for some Enums ([`16acd7d`](https://github.com/organize-rs/organize/commit/16acd7db44946ada7dabe166b7c5966ddf64370a)) - - Add aliases and refactor ([`0cda7dc`](https://github.com/organize-rs/organize/commit/0cda7dce78393c015bc0b72e0a6d50e5bfcf86dd)) - - Fixes to documention ([`1ae9d53`](https://github.com/organize-rs/organize/commit/1ae9d538745d8d624ad9c36b4c850a73c151bcf8)) - - Sketch out OrganizeFilter from documentation ([`27d7cdb`](https://github.com/organize-rs/organize/commit/27d7cdb8aca99cf758d4c532cc04942431e26bee)) - - Refine Recurse and Filters into Enum ([`5a35643`](https://github.com/organize-rs/organize/commit/5a35643d7d17e58691d325a3579744972e4e89c3)) - - Reorganize `actions` module ([`453c5ba`](https://github.com/organize-rs/organize/commit/453c5ba0c51cfea33165b33e22a354125b86af9a)) - - Sketch out OrganizeAction from documentation ([`f26488c`](https://github.com/organize-rs/organize/commit/f26488ce12b4f5709a104afc99b3362a304b19b1)) - - Implement `reveal` command ([`9083240`](https://github.com/organize-rs/organize/commit/908324034408452bd7387330928e9387e7d71aa1)) - - Add command stubs ([`b54a8a5`](https://github.com/organize-rs/organize/commit/b54a8a575abc8bf69e9946299c9bb076cc1b438e)) - - Fix clippy lints ([`3e36508`](https://github.com/organize-rs/organize/commit/3e365089de3a81292902f231ed9e4c19d885bf09)) - - Init abscissa app and generate config types from feldmann/organize ([`330a400`](https://github.com/organize-rs/organize/commit/330a400857b29282300034e191f830b21634125a)) -
+- **Uncategorized** + - Update Readme and add package details to Cargo.toml + ([`7265563`](https://github.com/organize-rs/organize/commit/72655635badb43ad473f82348ee0375e15f312d9)) + - Derive Deserialize and Serialize via Serde + ([`2fb46a5`](https://github.com/organize-rs/organize/commit/2fb46a595bf4cc4ebdb1e9a55ae88a036f45c8d3)) + - Derive Copy for some Enums + ([`16acd7d`](https://github.com/organize-rs/organize/commit/16acd7db44946ada7dabe166b7c5966ddf64370a)) + - Add aliases and refactor + ([`0cda7dc`](https://github.com/organize-rs/organize/commit/0cda7dce78393c015bc0b72e0a6d50e5bfcf86dd)) + - Fixes to documention + ([`1ae9d53`](https://github.com/organize-rs/organize/commit/1ae9d538745d8d624ad9c36b4c850a73c151bcf8)) + - Sketch out OrganizeFilter from documentation + ([`27d7cdb`](https://github.com/organize-rs/organize/commit/27d7cdb8aca99cf758d4c532cc04942431e26bee)) + - Refine Recurse and Filters into Enum + ([`5a35643`](https://github.com/organize-rs/organize/commit/5a35643d7d17e58691d325a3579744972e4e89c3)) + - Reorganize `actions` module + ([`453c5ba`](https://github.com/organize-rs/organize/commit/453c5ba0c51cfea33165b33e22a354125b86af9a)) + - Sketch out OrganizeAction from documentation + ([`f26488c`](https://github.com/organize-rs/organize/commit/f26488ce12b4f5709a104afc99b3362a304b19b1)) + - Implement `reveal` command + ([`9083240`](https://github.com/organize-rs/organize/commit/908324034408452bd7387330928e9387e7d71aa1)) + - Add command stubs + ([`b54a8a5`](https://github.com/organize-rs/organize/commit/b54a8a575abc8bf69e9946299c9bb076cc1b438e)) + - Fix clippy lints + ([`3e36508`](https://github.com/organize-rs/organize/commit/3e365089de3a81292902f231ed9e4c19d885bf09)) + - Init abscissa app and generate config types from feldmann/organize + ([`330a400`](https://github.com/organize-rs/organize/commit/330a400857b29282300034e191f830b21634125a)) + diff --git a/Cargo.toml b/Cargo.toml index c400764..1b2d9c2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,21 +10,21 @@ description = "organize - a file management automation tool" [package] name = "organize-rs" version = "0.4.1" -documentation = "https://docs.rs/organize-rs" -edition = { workspace = true } -description = { workspace = true } authors = { workspace = true } -repository = { workspace = true } -license = { workspace = true } categories = { workspace = true } -keywords = { workspace = true } +documentation = "https://docs.rs/organize-rs" +edition = { workspace = true } include = [ - "src/**/*", - "LICENSE", - "README.md", - "CHANGELOG.md", - "config/config_template.yaml", + "src/**/*", + "LICENSE", + "README.md", + "CHANGELOG.md", + "config/config_template.yaml", ] +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +description = { workspace = true } [workspace] members = ["crates/organize-rs_core", "crates/organize-rs_testing", "xtask"] @@ -34,9 +34,7 @@ default = [] research_organize = [] [workspace.dependencies] -organize-rs_core = { version = "0.2.0", path = "crates/organize-rs_core", features = [ - "cli", -] } +organize-rs_core = { version = "0.2.0", path = "crates/organize-rs_core", features = ["cli"] } organize-rs_testing = { path = "crates/organize-rs_testing" } clap = { version = "4.3.2", features = ["derive", "env", "wrap_help"] } directories = "5.0.1" @@ -46,12 +44,7 @@ thiserror = "1.0.40" anyhow = "1.0.71" once_cell = "1.18.0" duct = "0.13.6" -chrono = { version = "0.4.26", default-features = false, features = [ - "serde", - "clock", - "std", - "time", -] } +chrono = { version = "0.4.26", default-features = false, features = ["serde", "clock", "std", "time"] } jwalk = "0.8.1" regex = "1.8.4" @@ -79,7 +72,6 @@ filetime = "0.2.21" infer = "0.13" mime = "0.3" - # de-/serialisation serde = { version = "1.0.163", features = ["serde_derive"] } serde_with = "3.0.0" @@ -126,7 +118,6 @@ semver = "1.0.17" # cross-platform file-size on disk # https://crates.io/crates/filesize/0.2.0 - # detection of a file's MIME type by its extension # mime_guess = "2.0.4" @@ -147,38 +138,37 @@ semver = "1.0.17" # tui # ratatui = "0.20.1" # crossterm = "0.26.1" -# or use +# or use # tui-realm-stdlib = "^1.2.0" # tuirealm = "^1.8.0" [dependencies] -organize-rs_core = { workspace = true } +abscissa_core = { workspace = true } +aho-corasick = { workspace = true } +anyhow = { workspace = true } +chrono = { workspace = true } clap = { workspace = true } +clap_complete = { workspace = true } +dialoguer = { workspace = true } directories = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -thiserror = { workspace = true } -once_cell = { workspace = true } duct = { workspace = true } -chrono = { workspace = true } -regex = { workspace = true } -jwalk = { workspace = true } -serde_yaml = { workspace = true } -toml = { workspace = true } -abscissa_core = { workspace = true } -itertools = { workspace = true } -clap_complete = { workspace = true } indicatif = { workspace = true } -winnow = { workspace = true } +itertools = { workspace = true } +jwalk = { workspace = true } +once_cell = { workspace = true } +open = { workspace = true } +organize-rs_core = { workspace = true } +regex = { workspace = true } rhai = { workspace = true } -anyhow = { workspace = true } -dialoguer = { workspace = true } ron = { workspace = true } -open = { workspace = true } -serde_with = { workspace = true } semver = { workspace = true, features = ["serde"] } -aho-corasick = { workspace = true } - +serde = { workspace = true } +serde_json = { workspace = true } +serde_with = { workspace = true } +serde_yaml = { workspace = true } +thiserror = { workspace = true } +toml = { workspace = true } +winnow = { workspace = true } [dev-dependencies] abscissa_core = { workspace = true, features = ["testing"] } @@ -194,7 +184,7 @@ bench = true doc = true harness = true edition = "2021" -#required-features = [] +# required-features = [] [profile.dev] opt-level = 0 @@ -207,7 +197,7 @@ codegen-units = 4 [profile.release] opt-level = 3 panic = "abort" -debug = false # true for profiling +debug = false # true for profiling rpath = false lto = "fat" debug-assertions = false @@ -224,7 +214,7 @@ codegen-units = 4 [profile.bench] opt-level = 3 -debug = true # true for profiling +debug = true # true for profiling rpath = false lto = true debug-assertions = false diff --git a/README.md b/README.md index 5abbb69..f0d4871 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ -# organize [![AGPLv3+](https://www.gnu.org/graphics/agplv3-88x31.png)](https://www.gnu.org/licenses/agpl.txt) +# organize [![AGPLv3+](https://www.gnu.org/graphics/agplv3-88x31.png)](https://www.gnu.org/licenses/agpl.txt) -Crate Release Documentation +Crate Release +Documentation A file management automation tool. @@ -10,11 +11,15 @@ This is in really early development. Please come back later! ## Background -The Python [organize](https://organize.readthedocs.io/) is a file management automation tool. +The Python [organize](https://organize.readthedocs.io/) is a file management +automation tool. From their docs: -> Your desktop is a mess? You cannot find anything in your downloads and documents? Sorting and renaming all these files by hand is too tedious? Time to automate it once and benefit from it forever. -> organize is a command line, open-source alternative to apps like Hazel (macOS) or File Juggler (Windows). + +> Your desktop is a mess? You cannot find anything in your downloads and +> documents? Sorting and renaming all these files by hand is too tedious? Time +> to automate it once and benefit from it forever. organize is a command line, +> open-source alternative to apps like Hazel (macOS) or File Juggler (Windows). This is a Rust implementation of the same concept. @@ -30,29 +35,37 @@ This is a Rust implementation of the same concept. - `15MB..` => bigger than - `10KB..20MiB` => bigger than 10 KB, but smaller than 20 MiB - **NOTE**: You can use `decimal` (metric) and `binary` (IEC) multiple-byte units. E.g., `KiB` or `KB`, `GB` or `GiB`. They will be **converted** accordingly and are **case-insensitive**. + **NOTE**: You can use `decimal` (metric) and `binary` (IEC) multiple-byte + units. E.g., `KiB` or `KB`, `GB` or `GiB`. They will be **converted** + accordingly and are **case-insensitive**. - Filter `files` by their mimetype: `organize filter mimetype -l C:\organize\docs\screenshots -t files --mimetype image/jpeg` -- Filter `files` by their creation date (created in the last 5 days), ignore paths that have `target\` in their name, recursive, maximum `4` levels deep. +- Filter `files` by their creation date (created in the last 5 days), ignore + paths that have `target\` in their name, recursive, maximum `4` levels deep. `organize filter -r -m 4 created -l . -t files --ignore-path target\ --range ..5d` -- Filter `files`, which file stem ends with `go`, recursive, maximum `2` levels deep: +- Filter `files`, which file stem ends with `go`, recursive, maximum `2` levels + deep: `organize filter -r -m 2 name -l "C:\organize\" -t files --ends-with "go"` -- Filter `files` in `two locations`, which extensions match `rs` or `toml`, recursive, maximum `2` levels deep +- Filter `files` in `two locations`, which extensions match `rs` or `toml`, + recursive, maximum `2` levels deep `organize filter -r -m 2 extension -l C:\organize -l D:\folders -t files --exts rs --exts toml` -- Filter `files` and `folders`, which are empty (`0 bytes` or `no files` in directory), recursive, maximum `4` levels deep, ignore `git` in path names +- Filter `files` and `folders`, which are empty (`0 bytes` or `no files` in + directory), recursive, maximum `4` levels deep, ignore `git` in path names `organize filter -r -m 4 empty -l "C:\organize\" -t both --ignore-path git` -- Filter `files` and `folders`, which are empty (`0 bytes` or `no files` in directory), recursive, maximum `4` levels deep, ignore `git` only in file names +- Filter `files` and `folders`, which are empty (`0 bytes` or `no files` in + directory), recursive, maximum `4` levels deep, ignore `git` only in file + names `organize filter -r -m 4 empty -l "C:\organize\" -t both --ignore-name git` @@ -74,31 +87,43 @@ This is a Rust implementation of the same concept. ## Goals -For now the first goal for this Rust version of `organize` is to have feature parity with its Python equivalent. +For now the first goal for this Rust version of `organize` is to have feature +parity with its Python equivalent. -**BUT**: I want to take another approach on tackling the problem. It is also relatively complicated to map all the stuff -within a `config` file, because we are bound to the syntax of `yaml`/`json`/`toml`/`ron`. +**BUT**: I want to take another approach on tackling the problem. It is also +relatively complicated to map all the stuff within a `config` file, because we +are bound to the syntax of `yaml`/`json`/`toml`/`ron`. *Maybe this is exactly the problem to solve!* -Basically you want to have a configuration file, which replaces a mini scripting language. -With predefined `filters` and `actions` that are applied to the items that the filter spits out. +Basically you want to have a configuration file, which replaces a mini scripting +language. With predefined `filters` and `actions` that are applied to the items +that the filter spits out. -Basically almost everything in the configuration files are parameters for functions/methods. +Basically almost everything in the configuration files are parameters for +functions/methods. This makes everything more complicated. -1. What if we implement rusty `organize` in a way, that we can call `organize filter extension --ext "exe, msi, apk" --path {}` -and it spits out all the paths that match this `precoded` filter? -This way we can already utilize it easily within shell scripts. +1. What if we implement rusty `organize` in a way, that we can call + `organize filter extension --ext "exe, msi, apk" --path {}` and it spits out + all the paths that match this `precoded` filter? This way we can already + utilize it easily within shell scripts. -1. On the second implementation stage, we can embed a scripting engine like [rhai](https://crates.io/crates/rhai), where we expose some functionality of rusty `organize` as `functions` and register them with the `rhai` engine. +1. On the second implementation stage, we can embed a scripting engine like + [rhai](https://crates.io/crates/rhai), where we expose some functionality of + rusty `organize` as `functions` and register them with the `rhai` engine. -1. Instead of pressing everything in a complicated configuration file syntax, we can utilize a scripting language and boil it down to its minimum syntax. +1. Instead of pressing everything in a complicated configuration file syntax, we + can utilize a scripting language and boil it down to its minimum syntax. -That being said, a big factor for the Rust reiteration for me is that I like Rust. I want to reimplement `organize`'s approach in a language that has better error handling, and makes it easier to maintain software. That is fast and at the same time makes development less error prone. +That being said, a big factor for the Rust reiteration for me is that I like +Rust. I want to reimplement `organize`'s approach in a language that has better +error handling, and makes it easier to maintain software. That is fast and at +the same time makes development less error prone. -I'm the first user of the Rust implementation, and will be going to use it with my private files. Thus an important goal for me is stability. +I'm the first user of the Rust implementation, and will be going to use it with +my private files. Thus an important goal for me is stability. ## License diff --git a/TODO.md b/TODO.md index 839c32b..d910b31 100644 --- a/TODO.md +++ b/TODO.md @@ -3,7 +3,8 @@ ## Notes - Enums: -- FS actions impl: +- FS actions impl: + - Template impl - - @@ -14,93 +15,104 @@ ## Templating ```rust - fn call_command(tpe: FileType, id: &Id, filename: &Path, command: &str) -> RusticResult<()> { - let id = id.to_hex(); - let patterns = &["%file", "%type", "%id"]; - let ac = AhoCorasick::new(patterns).map_err(LocalErrorKind::FromAhoCorasick)?; - let replace_with = &[filename.to_str().unwrap(), tpe.into(), id.as_str()]; - let actual_command = ac.replace_all(command, replace_with); - debug!("calling {actual_command}..."); - let commands = parse_command::<()>(&actual_command) - .map_err(LocalErrorKind::FromNomError)? - .1; - let status = Command::new(commands[0]) - .args(&commands[1..]) - .status() - .map_err(LocalErrorKind::CommandExecutionFailed)?; - if !status.success() { - return Err(LocalErrorKind::CommandNotSuccessful { - file_name: replace_with[0].to_owned(), - file_type: replace_with[1].to_owned(), - id: replace_with[2].to_owned(), - status, - } - .into()); +fn call_command(tpe: FileType, id: &Id, filename: &Path, command: &str) -> RusticResult<()> { + let id = id.to_hex(); + let patterns = &["%file", "%type", "%id"]; + let ac = AhoCorasick::new(patterns).map_err(LocalErrorKind::FromAhoCorasick)?; + let replace_with = &[filename.to_str().unwrap(), tpe.into(), id.as_str()]; + let actual_command = ac.replace_all(command, replace_with); + debug!("calling {actual_command}..."); + let commands = parse_command::<()>(&actual_command) + .map_err(LocalErrorKind::FromNomError)? + .1; + let status = Command::new(commands[0]) + .args(&commands[1..]) + .status() + .map_err(LocalErrorKind::CommandExecutionFailed)?; + if !status.success() { + return Err(LocalErrorKind::CommandNotSuccessful { + file_name: replace_with[0].to_owned(), + file_type: replace_with[1].to_owned(), + id: replace_with[2].to_owned(), + status, } - Ok(()) + .into()); } + Ok(()) +} ``` ## Next 1. update screenshots and Readme.md 1. feat(templating): change arguments of some filters to be optional - - [APPROACH1] FilterKind::Inspect/FilterKind::Template - - and give information which template information we want to use (though we would still need to change return of `FilterClosures`) - - [APPROACH2] use `FilterKind` to lookup available template strings - - these can then be used in `actions` as e.g. `{{entry.size}}` - - lookup table: `[key: FilterKind -> available_templates: Vec]` + - [APPROACH1] FilterKind::Inspect/FilterKind::Template + - and give information which template information we want to use (though we + would still need to change return of `FilterClosures`) + - [APPROACH2] use `FilterKind` to lookup available template strings + - these can then be used in `actions` as e.g. `{{entry.size}}` + - lookup table: + `[key: FilterKind -> available_templates: Vec]` 1. generate example configs for integration test cases - - check against py-organize documentation for feature parity + - check against py-organize documentation for feature parity 1. implement unit tests for actions and the actions themselves 1. implement `organize run config` with preview and destructive run -1. implement `Alias` being just defined via `Vec` and used with the templating syntax `{{alias_name}}` - - ```yaml - aliases: - - !alias - name: my_music_folders - paths: - - path1 - - path2 - - etc - ``` - - and then something like - - ```yaml - locations: - - {{alias.my_music_folders}} - ``` - - - check places where it is applicable - - theoretically everywhere, where we take a `PathBuf` or `String` as part of a `Path` - - e.g. extensions, locations, ignore_name, ignore_path - - not usable in actions, or good error handling - - e.g. someone could use an alias for a `move::dst`, which would essentially copy the file to these places - - this should error out and actually be able to be checked beforehand, e.g. don't accept an alias that references more than one location string +1. implement `Alias` being just defined via `Vec` and used with the + templating syntax `{{alias_name}}` + + ```yaml + aliases: + - !alias + name: my_music_folders + paths: + - path1 + - path2 + - etc + ``` + + and then something like + + ```yaml + locations: + - {{alias.my_music_folders}} + ``` + + - check places where it is applicable + - theoretically everywhere, where we take a `PathBuf` or `String` as part of + a `Path` + - e.g. extensions, locations, ignore_name, ignore_path + - not usable in actions, or good error handling + - e.g. someone could use an alias for a `move::dst`, which would + essentially copy the file to these places + - this should error out and actually be able to be checked beforehand, + e.g. don't accept an alias that references more than one location + string ## Features 1. implement global ignore list - - .py - - .js - - .ini + - .py + - .js + - .ini 1. implement file name templating -1. support batch renaming (e.g. rename all images in a directory to image_n where n is a number - - remove some prefix from filenames - - we want to have filename tags as well e.g. `dnt_` (do not touch) or `wip_` so these things are treated differently - - also we want to have `{project_name}_` that we can remove after moving to a project folder - - do we really want to remove that? maybe give that as an option - `remove_keyword: bool` +1. support batch renaming (e.g. rename all images in a directory to image_n + where n is a number + - remove some prefix from filenames + - we want to have filename tags as well e.g. `dnt_` (do not touch) or + `wip_` so these things are treated differently + - also we want to have `{project_name}_` that we can remove after moving to + a project folder + - do we really want to remove that? maybe give that as an option + `remove_keyword: bool` 1. implement Terminal UI - - generate config interactively `generate config --interactive` - - + + - generate config interactively `generate config --interactive` + - + + 1. save state/event log between runs - - + - 1. implement scripting 1. build GUI @@ -116,9 +128,11 @@ ## General -- it would be reasonable to have each rule only result in `1` destructive action (e.g. move, trash, delete, rename) so conflicts are already minimized - - [RESEARCH] check which actions are parallelizable and for which actions it would make sense especially - - add confirmation dialog for manual conflict handling +- it would be reasonable to have each rule only result in `1` destructive action + (e.g. move, trash, delete, rename) so conflicts are already minimized + - [RESEARCH] check which actions are parallelizable and for which actions it + would make sense especially + - add confirmation dialog for manual conflict handling ## Filters @@ -127,7 +141,8 @@ - [ ] Regex - `Fancy_regex` + `regex` crates - `Vec>` to have the regex compiled at runtime, but only once - - Check regex implementation: + - Check regex implementation: + - [ ] Exif - `kamadak-exif` crate - @@ -135,9 +150,11 @@ - [ ] FileContent - [ ] Duplicate - we can utilize `itertools::unique_by` - - but that does already make it unique, so we need to see how we would collect the `DirEntry`, that was removed due to not being unique + - but that does already make it unique, so we need to see how we would + collect the `DirEntry`, that was removed due to not being unique - easy: compare against a `copy` - - we can use everything that is being exposed by the `DirEntry` itself (metadata.len(), filename(), etc.) + - we can use everything that is being exposed by the `DirEntry` itself + (metadata.len(), filename(), etc.) - [ ] BrokenLinks - matching soft-links whose files are non-existent - [ ] Added (OSX?) diff --git a/crates/organize-rs_core/CHANGELOG.md b/crates/organize-rs_core/CHANGELOG.md index 9efbf9c..e064327 100644 --- a/crates/organize-rs_core/CHANGELOG.md +++ b/crates/organize-rs_core/CHANGELOG.md @@ -3,41 +3,53 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +and this project adheres to +[Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.2.3 (2023-06-05) ### Documentation - - update config template with links to filters and actions in documentation +- update config template with + links to filters and actions in documentation ### New Features - - Reuse existing commands filter/action to output config snippets - - Implement confirmation dialogue before irreversibly applying an action - - implement previewing an action - - split the pipeline into several actors that could be run in parallel if needed +- Reuse existing commands + filter/action to output config snippets +- Implement confirmation + dialogue before irreversibly applying an action +- implement previewing an + action +- split the pipeline into + several actors that could be run in parallel if needed ### Bug Fixes - - Fix insta snapshot tests - - make doctests for actions pass - - make doctests for filters pass - - implement some more fixes for filter doctests - - fix syntax of filters in doc tests +- Fix insta snapshot tests +- make doctests for actions + pass +- make doctests for filters + pass +- implement some more fixes + for filter doctests +- fix syntax of filters in + doc tests ### Other - - add doctests for some actions +- add doctests for some + actions ### Commit Statistics - - 11 commits contributed to the release over the course of 2 calendar days. - - 2 days passed between releases. - - 11 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 11 commits contributed to the release over the course of 2 calendar days. +- 2 days passed between releases. +- 11 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -45,18 +57,31 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Reuse existing commands filter/action to output config snippets ([`ea7d264`](https://github.com/organize-rs/organize/commit/ea7d2648b7adc1fb4cef184f2a4b1c9b2868c46d)) - - Implement confirmation dialogue before irreversibly applying an action ([`975995c`](https://github.com/organize-rs/organize/commit/975995c36897e3c9e68c99daf065cc4c52f8b71c)) - - Implement previewing an action ([`37aad75`](https://github.com/organize-rs/organize/commit/37aad75d242f73091b3fc1058db28c40bbe6da70)) - - Split the pipeline into several actors that could be run in parallel if needed ([`27f0ad6`](https://github.com/organize-rs/organize/commit/27f0ad6f0613eee61df03bf8f2385cee94799f85)) - - Fix insta snapshot tests ([`ede34ff`](https://github.com/organize-rs/organize/commit/ede34ff4ce333005e9df9962cf971dcaf6039ef1)) - - Make doctests for actions pass ([`c73eca5`](https://github.com/organize-rs/organize/commit/c73eca59bcfd3a9163e624fc7eb318b9fb7364c2)) - - Add doctests for some actions ([`7144ddd`](https://github.com/organize-rs/organize/commit/7144ddda26a7a4368d5c5af3a3108df8217cee4f)) - - Make doctests for filters pass ([`8631500`](https://github.com/organize-rs/organize/commit/8631500aa5d4ea355cc824e7ac83c2045244d512)) - - Implement some more fixes for filter doctests ([`9eb60bb`](https://github.com/organize-rs/organize/commit/9eb60bbe08727df18baf2325bf7eab76e3cf28de)) - - Fix syntax of filters in doc tests ([`ecac2d3`](https://github.com/organize-rs/organize/commit/ecac2d3a398ca759db1afacb1f75c70a91c658eb)) - - Update config template with links to filters and actions in documentation ([`042926f`](https://github.com/organize-rs/organize/commit/042926f2d1b8bab5f24deb81456c917420d0ae73)) +- **Uncategorized** + - Reuse existing commands filter/action to output config snippets + ([`ea7d264`](https://github.com/organize-rs/organize/commit/ea7d2648b7adc1fb4cef184f2a4b1c9b2868c46d)) + - Implement confirmation dialogue before irreversibly applying an action + ([`975995c`](https://github.com/organize-rs/organize/commit/975995c36897e3c9e68c99daf065cc4c52f8b71c)) + - Implement previewing an action + ([`37aad75`](https://github.com/organize-rs/organize/commit/37aad75d242f73091b3fc1058db28c40bbe6da70)) + - Split the pipeline into several actors that could be run in parallel if + needed + ([`27f0ad6`](https://github.com/organize-rs/organize/commit/27f0ad6f0613eee61df03bf8f2385cee94799f85)) + - Fix insta snapshot tests + ([`ede34ff`](https://github.com/organize-rs/organize/commit/ede34ff4ce333005e9df9962cf971dcaf6039ef1)) + - Make doctests for actions pass + ([`c73eca5`](https://github.com/organize-rs/organize/commit/c73eca59bcfd3a9163e624fc7eb318b9fb7364c2)) + - Add doctests for some actions + ([`7144ddd`](https://github.com/organize-rs/organize/commit/7144ddda26a7a4368d5c5af3a3108df8217cee4f)) + - Make doctests for filters pass + ([`8631500`](https://github.com/organize-rs/organize/commit/8631500aa5d4ea355cc824e7ac83c2045244d512)) + - Implement some more fixes for filter doctests + ([`9eb60bb`](https://github.com/organize-rs/organize/commit/9eb60bbe08727df18baf2325bf7eab76e3cf28de)) + - Fix syntax of filters in doc tests + ([`ecac2d3`](https://github.com/organize-rs/organize/commit/ecac2d3a398ca759db1afacb1f75c70a91c658eb)) + - Update config template with links to filters and actions in documentation + ([`042926f`](https://github.com/organize-rs/organize/commit/042926f2d1b8bab5f24deb81456c917420d0ae73)) +
## 0.2.2 (2023-06-03) @@ -103,7 +128,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - @@ -146,87 +170,133 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Chore - - fully annotate template config - - cargo fix & cargo fmt +- fully annotate template + config +- cargo fix & cargo fmt ### Chore - - link config template on top level +- link config template on top + level ### Chore - - add license and config template to manifest and folder structure +- add license and config + template to manifest and folder structure ### Documentation - - add a bit more documentation for filters and actions - - start annotating a template config file - - add some thoughts about impl of actions +- add a bit more + documentation for filters and actions +- start annotating a template + config file +- add some thoughts about + impl of actions ### New Features - - consider tags when running rules from config - - implement parsing rules from config files - - add total entry count to output of filters - - adding cli option for filter mode - - Free the way for applying multiple filters +- consider tags when running + rules from config +- implement parsing rules + from config files +- add total entry count to + output of filters +- adding cli option for + filter mode +- Free the way for applying + multiple filters ### Bug Fixes - - chose better name for filter_groups - - make MaxDepth newtype transparent to serde +- chose better name for + filter_groups +- make MaxDepth newtype + transparent to serde ### Other - - add more doctests for filters - - add some doctests for filters - - add some doctests for filters - - check for tags when running rules from config file - - implement template generation - - implement some snapshot testing - - make lists comma separated in filters - - fix naming in config - - more state changes for runner - - implement first part of rule runner - - first implementation draft for a config run - - implement more of filesystem and action part - - : add concurrency and filesystem functionality - - more unit tests for matches_date - - implement unit tests for date matching - - add unit test for mimetype - - fix unit test - - implement more unit tests for name filter - - implement more test cases for name filter - - add more unit tests for filters - - parse first parts of py-organize config - - Switch to jwalk to walk the directory tree parallelized - - apply multiple filters to walkdir iter +- add more doctests for + filters +- add some doctests for + filters +- add some doctests for + filters +- check for tags when running + rules from config file +- implement template + generation +- implement some snapshot + testing +- make lists comma separated + in filters +- fix naming in config +- more state changes for + runner +- implement first part of + rule runner +- first implementation draft + for a config run +- implement more of + filesystem and action part +- : add concurrency and + filesystem functionality +- more unit tests for + matches_date +- implement unit tests for + date matching +- add unit test for mimetype +- fix unit test +- implement more unit tests + for name filter +- implement more test cases + for name filter +- add more unit tests for + filters +- parse first parts of + py-organize config +- Switch to jwalk to walk the + directory tree parallelized +- apply multiple filters to + walkdir iter ### Refactor - - add branch itself to `tag_applies` method - - Move MAX_DEPTH to associated constant - - units tests to be less repetitive - - to filetime for platform-agnostic atime, ctime, mtime inspection - - split filters in seperate modules - - flatten library structure - - change Rule to contain new types - - add scripting and shell filter, add check for config and scripts subcommand, and run subcommands for config and scripts, add generate subcommand - - more functional syntax - - implement custom serializer for MaxDepth - - Add associated constants for SizeRange and PeriodRange - - improve display - - Make FilterWalker more configurable for upcoming features - - make filters use more option/result methods +- add branch itself to + `tag_applies` method +- Move MAX_DEPTH to + associated constant +- units tests to be less + repetitive +- to filetime for + platform-agnostic atime, ctime, mtime inspection +- split filters in seperate + modules +- flatten library structure +- change Rule to contain new + types +- add scripting and shell + filter, add check for config and scripts subcommand, and run subcommands for + config and scripts, add generate subcommand +- more functional syntax +- implement custom serializer + for MaxDepth +- Add associated constants + for SizeRange and PeriodRange +- improve display +- Make FilterWalker more + configurable for upcoming features +- make filters use more + option/result methods ### Commit Statistics - - 60 commits contributed to the release over the course of 9 calendar days. - - 9 days passed between releases. - - 51 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 60 commits contributed to the release over the course of 9 calendar days. +- 9 days passed between releases. +- 51 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -234,67 +304,129 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Release organize-rs_core v0.2.2, organize-rs v0.4.0 ([`ccc3ad1`](https://github.com/organize-rs/organize/commit/ccc3ad1bd579f8407d6267e31562e86352d7df57)) - - Link config template on top level ([`5672b52`](https://github.com/organize-rs/organize/commit/5672b52473d2715ba320dc9e6aed45119e7b0eae)) - - Release organize-rs_core v0.2.2 ([`f13bef7`](https://github.com/organize-rs/organize/commit/f13bef7bc2aef5a499a8f340dcd3ba27ccd41f5f)) - - Release organize-rs_core v0.2.2, organize-rs v0.4.0 ([`38b98b8`](https://github.com/organize-rs/organize/commit/38b98b805dd984773c7445d4b3bcdcfe62965596)) - - Add license and config template to manifest and folder structure ([`44a037f`](https://github.com/organize-rs/organize/commit/44a037fa00212ec2d07a0449f9b3d0aee05e3d55)) - - Release organize-rs_core v0.2.2, organize-rs v0.4.0 ([`ed623d7`](https://github.com/organize-rs/organize/commit/ed623d73fa574d4641e11e27cf6a2d4936ed5abe)) - - Release organize-rs_core v0.2.2, organize-rs v0.4.0 ([`27615a9`](https://github.com/organize-rs/organize/commit/27615a970fd946871a2c64f151490bc0269a45a5)) - - Release organize-rs_core v0.2.2, organize-rs v0.4.0 ([`8b71b45`](https://github.com/organize-rs/organize/commit/8b71b4546f100d0003c76f80f1d6c6d07615f74e)) - - Add a bit more documentation for filters and actions ([`e39e96c`](https://github.com/organize-rs/organize/commit/e39e96c5b5b08ecad0cb197c62f49fd0809faabf)) - - Add more doctests for filters ([`a7d737c`](https://github.com/organize-rs/organize/commit/a7d737c5cc9e6465c7cf724c5d020b0f7e8ad935)) - - Add some doctests for filters ([`a024a32`](https://github.com/organize-rs/organize/commit/a024a32327344ff109d8e6e68819f507d7c9e8ab)) - - Add some doctests for filters ([`28ad000`](https://github.com/organize-rs/organize/commit/28ad00049f14ec1761f26baf2fe2088854094900)) - - Fully annotate template config ([`930593f`](https://github.com/organize-rs/organize/commit/930593f55f0128609daf98ccee901d5e33c29223)) - - Add branch itself to `tag_applies` method ([`b633c56`](https://github.com/organize-rs/organize/commit/b633c561839314c760bf9a1a70e5b1aadac23b12)) - - Consider tags when running rules from config ([`f6bfca7`](https://github.com/organize-rs/organize/commit/f6bfca7440be8ee3f3aeed24b3164458cd2dcfe9)) - - Check for tags when running rules from config file ([`7c0b0c9`](https://github.com/organize-rs/organize/commit/7c0b0c9ce915e8b43d4542bc79b53432dd5bc2d2)) - - Cargo fix & cargo fmt ([`cb1e115`](https://github.com/organize-rs/organize/commit/cb1e1151c09afed48f40b989132ae89324f9f2b1)) - - Implement template generation ([`19110b9`](https://github.com/organize-rs/organize/commit/19110b9592fb35314402f9bac71f52791c5a7cd1)) - - Start annotating a template config file ([`0f49e89`](https://github.com/organize-rs/organize/commit/0f49e89282eaa8fc5f11a1db81553484e9a669b9)) - - Implement some snapshot testing ([`ad6f684`](https://github.com/organize-rs/organize/commit/ad6f6844dc93dac07e4d1921d1dbe0ab984b15c9)) - - Implement parsing rules from config files ([`94757b5`](https://github.com/organize-rs/organize/commit/94757b5e2eddb54b0a706634f408bb5fcabfaf94)) - - Chose better name for filter_groups ([`45182d5`](https://github.com/organize-rs/organize/commit/45182d5fa76f1716c98ce372aea4177ef6d3bb50)) - - Make lists comma separated in filters ([`b665490`](https://github.com/organize-rs/organize/commit/b665490feb2a7a44996847d80ee918ea82cc4772)) - - Fix naming in config ([`22192eb`](https://github.com/organize-rs/organize/commit/22192eb528c2f95ad01ef124082dfae518e8c464)) - - More state changes for runner ([`afb116b`](https://github.com/organize-rs/organize/commit/afb116bc91add3ac76f6c55e44932391eec6e7e8)) - - Implement first part of rule runner ([`3f6df32`](https://github.com/organize-rs/organize/commit/3f6df32eac07d369b035ffb06156c2b47b0f9121)) - - Move MAX_DEPTH to associated constant ([`2dec903`](https://github.com/organize-rs/organize/commit/2dec9033ce8187cdb74b23e2363d09d65131bbdd)) - - Make MaxDepth newtype transparent to serde ([`ef8ea62`](https://github.com/organize-rs/organize/commit/ef8ea621bd5fcbdb24050204ef77eb2e330cf880)) - - First implementation draft for a config run ([`abdc5ec`](https://github.com/organize-rs/organize/commit/abdc5ecfe89c8db0b3f7077681ef3a23a7797df5)) - - Implement more of filesystem and action part ([`c2a5e01`](https://github.com/organize-rs/organize/commit/c2a5e017868e9f2af2bda3576b7e342356783722)) - - : add concurrency and filesystem functionality ([`67f506e`](https://github.com/organize-rs/organize/commit/67f506ea0f869bb0a55c1d3938d3d8b58612c979)) - - Units tests to be less repetitive ([`5b35248`](https://github.com/organize-rs/organize/commit/5b35248f532666f09dd8fd9308be1d16cb7c9f81)) - - More unit tests for matches_date ([`a52abf6`](https://github.com/organize-rs/organize/commit/a52abf6e5aa902c22c3fd7eebdd3088a2b3c6a50)) - - Implement unit tests for date matching ([`2abf8ea`](https://github.com/organize-rs/organize/commit/2abf8ea4fb19e8d1be640fb52abe1aba9c4e08c4)) - - To filetime for platform-agnostic atime, ctime, mtime inspection ([`f4a9cfa`](https://github.com/organize-rs/organize/commit/f4a9cfaa19d9f088768f2f705f14a400ad701a8b)) - - Add some thoughts about impl of actions ([`96c3d95`](https://github.com/organize-rs/organize/commit/96c3d950812a8df8cdccb53a47ae0e6165462117)) - - Add unit test for mimetype ([`db912c4`](https://github.com/organize-rs/organize/commit/db912c4ccfbc525ff4c5dccf231fda064e9a001e)) - - Fix unit test ([`2a75ebb`](https://github.com/organize-rs/organize/commit/2a75ebb8b239d79e02262e5aeb470c66b25afbb9)) - - Implement more unit tests for name filter ([`2d6ad42`](https://github.com/organize-rs/organize/commit/2d6ad427618ab76d5b73471dba62f6280218f63e)) - - Implement more test cases for name filter ([`31c790a`](https://github.com/organize-rs/organize/commit/31c790a1b244c5b98de9bba21b5ed9052d47d788)) - - Split filters in seperate modules ([`85fd452`](https://github.com/organize-rs/organize/commit/85fd452e00835f14e84da9547bb5659449771cde)) - - Add more unit tests for filters ([`db2d348`](https://github.com/organize-rs/organize/commit/db2d3480a1f965e861f92b8df04079461ee2f35d)) - - More deserialization attempts ([`c32b4b3`](https://github.com/organize-rs/organize/commit/c32b4b30c602b61d5ce94a037ccf2a496232c935)) - - Flatten library structure ([`8b47644`](https://github.com/organize-rs/organize/commit/8b47644af39c99f79658cdd54c067f9787b02347)) - - Parse first parts of py-organize config ([`91a8840`](https://github.com/organize-rs/organize/commit/91a8840c5826eb39493384a3e2d1bbb69f38c298)) - - Change Rule to contain new types ([`2c06e62`](https://github.com/organize-rs/organize/commit/2c06e62ede4f1de31c75dd786c65f87b0855dffc)) - - Add scripting and shell filter, add check for config and scripts subcommand, and run subcommands for config and scripts, add generate subcommand ([`54a8993`](https://github.com/organize-rs/organize/commit/54a8993981273a39fb0da07de5853a2fbc5764b4)) - - Release organize-rs_core v0.2.1, organize-rs v0.3.1 ([`e9689a9`](https://github.com/organize-rs/organize/commit/e9689a90133a48eaa76cd6d51aa4a8b3b496a868)) - - Add total entry count to output of filters ([`2c55b20`](https://github.com/organize-rs/organize/commit/2c55b20c24da830f5d81468450882f126c8d5531)) - - More functional syntax ([`0c79c6b`](https://github.com/organize-rs/organize/commit/0c79c6b5b75adc05c54fee49f3398ed871ffd9b3)) - - Switch to jwalk to walk the directory tree parallelized ([`18ee702`](https://github.com/organize-rs/organize/commit/18ee7029fa931b0f95046f11c2919b23f11c1470)) - - Implement custom serializer for MaxDepth ([`95d7740`](https://github.com/organize-rs/organize/commit/95d7740dfbda24d934eea7ecce21b39076cf251c)) - - Add associated constants for SizeRange and PeriodRange ([`847737a`](https://github.com/organize-rs/organize/commit/847737a5554f76a76366cd67fe8b2870277c935b)) - - Improve display ([`99d7b0c`](https://github.com/organize-rs/organize/commit/99d7b0c109017a78c1ef726dcc9d40d0507b0426)) - - Adding cli option for filter mode ([`d088c50`](https://github.com/organize-rs/organize/commit/d088c50c894a9a94a47b2e3b66502fa3d5fdfc59)) - - Make FilterWalker more configurable for upcoming features ([`a82a208`](https://github.com/organize-rs/organize/commit/a82a20821ebe60d386efcb9856f4d22b45ab4a2e)) - - Free the way for applying multiple filters ([`7c29918`](https://github.com/organize-rs/organize/commit/7c2991827f503c7caf6f82e964f9cc91f5c39d0e)) - - Fix borrow issues ([`398edb2`](https://github.com/organize-rs/organize/commit/398edb29916398c43b15531779233eda4b28eef5)) - - Apply multiple filters to walkdir iter ([`a316302`](https://github.com/organize-rs/organize/commit/a3163027800f1843476b8855a41f2115fdb3d3eb)) - - Make filters use more option/result methods ([`fd90047`](https://github.com/organize-rs/organize/commit/fd90047424eb9e6f04481a1ef35825e360b06912)) +- **Uncategorized** + - Release organize-rs_core v0.2.2, organize-rs v0.4.0 + ([`ccc3ad1`](https://github.com/organize-rs/organize/commit/ccc3ad1bd579f8407d6267e31562e86352d7df57)) + - Link config template on top level + ([`5672b52`](https://github.com/organize-rs/organize/commit/5672b52473d2715ba320dc9e6aed45119e7b0eae)) + - Release organize-rs_core v0.2.2 + ([`f13bef7`](https://github.com/organize-rs/organize/commit/f13bef7bc2aef5a499a8f340dcd3ba27ccd41f5f)) + - Release organize-rs_core v0.2.2, organize-rs v0.4.0 + ([`38b98b8`](https://github.com/organize-rs/organize/commit/38b98b805dd984773c7445d4b3bcdcfe62965596)) + - Add license and config template to manifest and folder structure + ([`44a037f`](https://github.com/organize-rs/organize/commit/44a037fa00212ec2d07a0449f9b3d0aee05e3d55)) + - Release organize-rs_core v0.2.2, organize-rs v0.4.0 + ([`ed623d7`](https://github.com/organize-rs/organize/commit/ed623d73fa574d4641e11e27cf6a2d4936ed5abe)) + - Release organize-rs_core v0.2.2, organize-rs v0.4.0 + ([`27615a9`](https://github.com/organize-rs/organize/commit/27615a970fd946871a2c64f151490bc0269a45a5)) + - Release organize-rs_core v0.2.2, organize-rs v0.4.0 + ([`8b71b45`](https://github.com/organize-rs/organize/commit/8b71b4546f100d0003c76f80f1d6c6d07615f74e)) + - Add a bit more documentation for filters and actions + ([`e39e96c`](https://github.com/organize-rs/organize/commit/e39e96c5b5b08ecad0cb197c62f49fd0809faabf)) + - Add more doctests for filters + ([`a7d737c`](https://github.com/organize-rs/organize/commit/a7d737c5cc9e6465c7cf724c5d020b0f7e8ad935)) + - Add some doctests for filters + ([`a024a32`](https://github.com/organize-rs/organize/commit/a024a32327344ff109d8e6e68819f507d7c9e8ab)) + - Add some doctests for filters + ([`28ad000`](https://github.com/organize-rs/organize/commit/28ad00049f14ec1761f26baf2fe2088854094900)) + - Fully annotate template config + ([`930593f`](https://github.com/organize-rs/organize/commit/930593f55f0128609daf98ccee901d5e33c29223)) + - Add branch itself to `tag_applies` method + ([`b633c56`](https://github.com/organize-rs/organize/commit/b633c561839314c760bf9a1a70e5b1aadac23b12)) + - Consider tags when running rules from config + ([`f6bfca7`](https://github.com/organize-rs/organize/commit/f6bfca7440be8ee3f3aeed24b3164458cd2dcfe9)) + - Check for tags when running rules from config file + ([`7c0b0c9`](https://github.com/organize-rs/organize/commit/7c0b0c9ce915e8b43d4542bc79b53432dd5bc2d2)) + - Cargo fix & cargo fmt + ([`cb1e115`](https://github.com/organize-rs/organize/commit/cb1e1151c09afed48f40b989132ae89324f9f2b1)) + - Implement template generation + ([`19110b9`](https://github.com/organize-rs/organize/commit/19110b9592fb35314402f9bac71f52791c5a7cd1)) + - Start annotating a template config file + ([`0f49e89`](https://github.com/organize-rs/organize/commit/0f49e89282eaa8fc5f11a1db81553484e9a669b9)) + - Implement some snapshot testing + ([`ad6f684`](https://github.com/organize-rs/organize/commit/ad6f6844dc93dac07e4d1921d1dbe0ab984b15c9)) + - Implement parsing rules from config files + ([`94757b5`](https://github.com/organize-rs/organize/commit/94757b5e2eddb54b0a706634f408bb5fcabfaf94)) + - Chose better name for filter_groups + ([`45182d5`](https://github.com/organize-rs/organize/commit/45182d5fa76f1716c98ce372aea4177ef6d3bb50)) + - Make lists comma separated in filters + ([`b665490`](https://github.com/organize-rs/organize/commit/b665490feb2a7a44996847d80ee918ea82cc4772)) + - Fix naming in config + ([`22192eb`](https://github.com/organize-rs/organize/commit/22192eb528c2f95ad01ef124082dfae518e8c464)) + - More state changes for runner + ([`afb116b`](https://github.com/organize-rs/organize/commit/afb116bc91add3ac76f6c55e44932391eec6e7e8)) + - Implement first part of rule runner + ([`3f6df32`](https://github.com/organize-rs/organize/commit/3f6df32eac07d369b035ffb06156c2b47b0f9121)) + - Move MAX_DEPTH to associated constant + ([`2dec903`](https://github.com/organize-rs/organize/commit/2dec9033ce8187cdb74b23e2363d09d65131bbdd)) + - Make MaxDepth newtype transparent to serde + ([`ef8ea62`](https://github.com/organize-rs/organize/commit/ef8ea621bd5fcbdb24050204ef77eb2e330cf880)) + - First implementation draft for a config run + ([`abdc5ec`](https://github.com/organize-rs/organize/commit/abdc5ecfe89c8db0b3f7077681ef3a23a7797df5)) + - Implement more of filesystem and action part + ([`c2a5e01`](https://github.com/organize-rs/organize/commit/c2a5e017868e9f2af2bda3576b7e342356783722)) + - : add concurrency and filesystem functionality + ([`67f506e`](https://github.com/organize-rs/organize/commit/67f506ea0f869bb0a55c1d3938d3d8b58612c979)) + - Units tests to be less repetitive + ([`5b35248`](https://github.com/organize-rs/organize/commit/5b35248f532666f09dd8fd9308be1d16cb7c9f81)) + - More unit tests for matches_date + ([`a52abf6`](https://github.com/organize-rs/organize/commit/a52abf6e5aa902c22c3fd7eebdd3088a2b3c6a50)) + - Implement unit tests for date matching + ([`2abf8ea`](https://github.com/organize-rs/organize/commit/2abf8ea4fb19e8d1be640fb52abe1aba9c4e08c4)) + - To filetime for platform-agnostic atime, ctime, mtime inspection + ([`f4a9cfa`](https://github.com/organize-rs/organize/commit/f4a9cfaa19d9f088768f2f705f14a400ad701a8b)) + - Add some thoughts about impl of actions + ([`96c3d95`](https://github.com/organize-rs/organize/commit/96c3d950812a8df8cdccb53a47ae0e6165462117)) + - Add unit test for mimetype + ([`db912c4`](https://github.com/organize-rs/organize/commit/db912c4ccfbc525ff4c5dccf231fda064e9a001e)) + - Fix unit test + ([`2a75ebb`](https://github.com/organize-rs/organize/commit/2a75ebb8b239d79e02262e5aeb470c66b25afbb9)) + - Implement more unit tests for name filter + ([`2d6ad42`](https://github.com/organize-rs/organize/commit/2d6ad427618ab76d5b73471dba62f6280218f63e)) + - Implement more test cases for name filter + ([`31c790a`](https://github.com/organize-rs/organize/commit/31c790a1b244c5b98de9bba21b5ed9052d47d788)) + - Split filters in seperate modules + ([`85fd452`](https://github.com/organize-rs/organize/commit/85fd452e00835f14e84da9547bb5659449771cde)) + - Add more unit tests for filters + ([`db2d348`](https://github.com/organize-rs/organize/commit/db2d3480a1f965e861f92b8df04079461ee2f35d)) + - More deserialization attempts + ([`c32b4b3`](https://github.com/organize-rs/organize/commit/c32b4b30c602b61d5ce94a037ccf2a496232c935)) + - Flatten library structure + ([`8b47644`](https://github.com/organize-rs/organize/commit/8b47644af39c99f79658cdd54c067f9787b02347)) + - Parse first parts of py-organize config + ([`91a8840`](https://github.com/organize-rs/organize/commit/91a8840c5826eb39493384a3e2d1bbb69f38c298)) + - Change Rule to contain new types + ([`2c06e62`](https://github.com/organize-rs/organize/commit/2c06e62ede4f1de31c75dd786c65f87b0855dffc)) + - Add scripting and shell filter, add check for config and scripts subcommand, + and run subcommands for config and scripts, add generate subcommand + ([`54a8993`](https://github.com/organize-rs/organize/commit/54a8993981273a39fb0da07de5853a2fbc5764b4)) + - Release organize-rs_core v0.2.1, organize-rs v0.3.1 + ([`e9689a9`](https://github.com/organize-rs/organize/commit/e9689a90133a48eaa76cd6d51aa4a8b3b496a868)) + - Add total entry count to output of filters + ([`2c55b20`](https://github.com/organize-rs/organize/commit/2c55b20c24da830f5d81468450882f126c8d5531)) + - More functional syntax + ([`0c79c6b`](https://github.com/organize-rs/organize/commit/0c79c6b5b75adc05c54fee49f3398ed871ffd9b3)) + - Switch to jwalk to walk the directory tree parallelized + ([`18ee702`](https://github.com/organize-rs/organize/commit/18ee7029fa931b0f95046f11c2919b23f11c1470)) + - Implement custom serializer for MaxDepth + ([`95d7740`](https://github.com/organize-rs/organize/commit/95d7740dfbda24d934eea7ecce21b39076cf251c)) + - Add associated constants for SizeRange and PeriodRange + ([`847737a`](https://github.com/organize-rs/organize/commit/847737a5554f76a76366cd67fe8b2870277c935b)) + - Improve display + ([`99d7b0c`](https://github.com/organize-rs/organize/commit/99d7b0c109017a78c1ef726dcc9d40d0507b0426)) + - Adding cli option for filter mode + ([`d088c50`](https://github.com/organize-rs/organize/commit/d088c50c894a9a94a47b2e3b66502fa3d5fdfc59)) + - Make FilterWalker more configurable for upcoming features + ([`a82a208`](https://github.com/organize-rs/organize/commit/a82a20821ebe60d386efcb9856f4d22b45ab4a2e)) + - Free the way for applying multiple filters + ([`7c29918`](https://github.com/organize-rs/organize/commit/7c2991827f503c7caf6f82e964f9cc91f5c39d0e)) + - Fix borrow issues + ([`398edb2`](https://github.com/organize-rs/organize/commit/398edb29916398c43b15531779233eda4b28eef5)) + - Apply multiple filters to walkdir iter + ([`a316302`](https://github.com/organize-rs/organize/commit/a3163027800f1843476b8855a41f2115fdb3d3eb)) + - Make filters use more option/result methods + ([`fd90047`](https://github.com/organize-rs/organize/commit/fd90047424eb9e6f04481a1ef35825e360b06912)) +
## 0.2.1 (2023-05-25) @@ -308,7 +440,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - @@ -319,32 +450,42 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### New Features - - add total entry count to output of filters - - adding cli option for filter mode - - Free the way for applying multiple filters +- add total entry count to + output of filters +- adding cli option for + filter mode +- Free the way for applying + multiple filters ### Other - - Switch to jwalk to walk the directory tree parallelized - - apply multiple filters to walkdir iter +- Switch to jwalk to walk the + directory tree parallelized +- apply multiple filters to + walkdir iter ### Refactor - - more functional syntax - - implement custom serializer for MaxDepth - - Add associated constants for SizeRange and PeriodRange - - improve display - - Make FilterWalker more configurable for upcoming features - - make filters use more option/result methods +- more functional syntax +- implement custom serializer + for MaxDepth +- Add associated constants + for SizeRange and PeriodRange +- improve display +- Make FilterWalker more + configurable for upcoming features +- make filters use more + option/result methods ### Commit Statistics - - 13 commits contributed to the release over the course of 2 calendar days. - - 3 days passed between releases. - - 11 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 13 commits contributed to the release over the course of 2 calendar days. +- 3 days passed between releases. +- 11 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -352,35 +493,51 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Release organize-rs_core v0.2.1, organize-rs v0.3.1 ([`2ff132c`](https://github.com/organize-rs/organize/commit/2ff132c615a868856281bffdfd84de35b848a365)) - - Add total entry count to output of filters ([`12b5e9c`](https://github.com/organize-rs/organize/commit/12b5e9cb07d2258ac4a56ce7d38993802e6f7385)) - - More functional syntax ([`48dd7da`](https://github.com/organize-rs/organize/commit/48dd7daf79444deee11134c5c42fe4e9aa4e18ba)) - - Switch to jwalk to walk the directory tree parallelized ([`4757013`](https://github.com/organize-rs/organize/commit/4757013ebe9fe5d37ea3d7b7cddf155910e0f5b4)) - - Implement custom serializer for MaxDepth ([`8504e82`](https://github.com/organize-rs/organize/commit/8504e820eca6dd8be66d707695e56d0c8c8f3be6)) - - Add associated constants for SizeRange and PeriodRange ([`9d55a01`](https://github.com/organize-rs/organize/commit/9d55a017ae9ba14b726fe542a9354fcf045b06f3)) - - Improve display ([`543150d`](https://github.com/organize-rs/organize/commit/543150dd96aca886bacd0057bda1957d19b4322d)) - - Adding cli option for filter mode ([`fea4cc1`](https://github.com/organize-rs/organize/commit/fea4cc14fe1f64cc9fd91664bf07f8940cba15a1)) - - Make FilterWalker more configurable for upcoming features ([`14300ea`](https://github.com/organize-rs/organize/commit/14300ea60bcccf500d813ee267792899a278a9ff)) - - Free the way for applying multiple filters ([`33fc910`](https://github.com/organize-rs/organize/commit/33fc910001ac35967bc1e424b110c88ace6b9186)) - - Fix borrow issues ([`3d1b0a1`](https://github.com/organize-rs/organize/commit/3d1b0a19e71441bdec6a9b609833b91a8ef890d8)) - - Apply multiple filters to walkdir iter ([`09c428c`](https://github.com/organize-rs/organize/commit/09c428cc45bbb348ca08e5fd233c999408ca2500)) - - Make filters use more option/result methods ([`c005013`](https://github.com/organize-rs/organize/commit/c005013d1c49f5d717d635c3ece760bb5c904e09)) +- **Uncategorized** + - Release organize-rs_core v0.2.1, organize-rs v0.3.1 + ([`2ff132c`](https://github.com/organize-rs/organize/commit/2ff132c615a868856281bffdfd84de35b848a365)) + - Add total entry count to output of filters + ([`12b5e9c`](https://github.com/organize-rs/organize/commit/12b5e9cb07d2258ac4a56ce7d38993802e6f7385)) + - More functional syntax + ([`48dd7da`](https://github.com/organize-rs/organize/commit/48dd7daf79444deee11134c5c42fe4e9aa4e18ba)) + - Switch to jwalk to walk the directory tree parallelized + ([`4757013`](https://github.com/organize-rs/organize/commit/4757013ebe9fe5d37ea3d7b7cddf155910e0f5b4)) + - Implement custom serializer for MaxDepth + ([`8504e82`](https://github.com/organize-rs/organize/commit/8504e820eca6dd8be66d707695e56d0c8c8f3be6)) + - Add associated constants for SizeRange and PeriodRange + ([`9d55a01`](https://github.com/organize-rs/organize/commit/9d55a017ae9ba14b726fe542a9354fcf045b06f3)) + - Improve display + ([`543150d`](https://github.com/organize-rs/organize/commit/543150dd96aca886bacd0057bda1957d19b4322d)) + - Adding cli option for filter mode + ([`fea4cc1`](https://github.com/organize-rs/organize/commit/fea4cc14fe1f64cc9fd91664bf07f8940cba15a1)) + - Make FilterWalker more configurable for upcoming features + ([`14300ea`](https://github.com/organize-rs/organize/commit/14300ea60bcccf500d813ee267792899a278a9ff)) + - Free the way for applying multiple filters + ([`33fc910`](https://github.com/organize-rs/organize/commit/33fc910001ac35967bc1e424b110c88ace6b9186)) + - Fix borrow issues + ([`3d1b0a1`](https://github.com/organize-rs/organize/commit/3d1b0a19e71441bdec6a9b609833b91a8ef890d8)) + - Apply multiple filters to walkdir iter + ([`09c428c`](https://github.com/organize-rs/organize/commit/09c428cc45bbb348ca08e5fd233c999408ca2500)) + - Make filters use more option/result methods + ([`c005013`](https://github.com/organize-rs/organize/commit/c005013d1c49f5d717d635c3ece760bb5c904e09)) +
## 0.2.0 (2023-05-21) ### New Features (BREAKING) - - implement range syntax also for date related filters +- implement range syntax also + for date related filters ### Commit Statistics - - 2 commits contributed to the release. - - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 2 commits contributed to the release. +- 1 commit was understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -388,25 +545,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Release organize-rs_core v0.2.0, safety bump organize-rs v0.3.0 ([`fb27edc`](https://github.com/organize-rs/organize/commit/fb27edc1f40e49e4db6d8553ebb1317feb0cb8be)) - - Implement range syntax also for date related filters ([`13f7560`](https://github.com/organize-rs/organize/commit/13f7560c1deb053fb74880247d01b60be6ef0ca1)) +- **Uncategorized** + - Release organize-rs_core v0.2.0, safety bump organize-rs v0.3.0 + ([`fb27edc`](https://github.com/organize-rs/organize/commit/fb27edc1f40e49e4db6d8553ebb1317feb0cb8be)) + - Implement range syntax also for date related filters + ([`13f7560`](https://github.com/organize-rs/organize/commit/13f7560c1deb053fb74880247d01b60be6ef0ca1)) +
## 0.1.6 (2023-05-21) ### New Features - - Implement `size` filter - - Implement parser for byte size conditions for `size` filter +- Implement `size` filter +- Implement parser for byte + size conditions for `size` filter ### Commit Statistics - - 4 commits contributed to the release. - - 2 commits were understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 4 commits contributed to the release. +- 2 commits were understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -414,26 +576,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Release organize-rs_core v0.1.6 ([`fc39cd1`](https://github.com/organize-rs/organize/commit/fc39cd180ae6c20023e3a821f2156eaf953a8729)) - - Merge branch 'develop' ([`7bf59e8`](https://github.com/organize-rs/organize/commit/7bf59e8bfb8da7033192034153a9216a2db185c4)) - - Implement `size` filter ([`da1d07a`](https://github.com/organize-rs/organize/commit/da1d07a58a6ccafc1f930132eb0cb4c182af9569)) - - Implement parser for byte size conditions for `size` filter ([`0cf330e`](https://github.com/organize-rs/organize/commit/0cf330e125e4154f71cef3c990ab574928cf49d1)) +- **Uncategorized** + - Release organize-rs_core v0.1.6 + ([`fc39cd1`](https://github.com/organize-rs/organize/commit/fc39cd180ae6c20023e3a821f2156eaf953a8729)) + - Merge branch 'develop' + ([`7bf59e8`](https://github.com/organize-rs/organize/commit/7bf59e8bfb8da7033192034153a9216a2db185c4)) + - Implement `size` filter + ([`da1d07a`](https://github.com/organize-rs/organize/commit/da1d07a58a6ccafc1f930132eb0cb4c182af9569)) + - Implement parser for byte size conditions for `size` filter + ([`0cf330e`](https://github.com/organize-rs/organize/commit/0cf330e125e4154f71cef3c990ab574928cf49d1)) +
## 0.1.5 (2023-05-20) ### New Features - - Implement `mimetype` filter +- Implement `mimetype` filter ### Commit Statistics - - 33 commits contributed to the release over the course of 5 calendar days. - - 1 commit was understood as [conventional](https://www.conventionalcommits.org). - - 0 issues like '(#ID)' were seen in commit messages +- 33 commits contributed to the release over the course of 5 calendar days. +- 1 commit was understood as + [conventional](https://www.conventionalcommits.org). +- 0 issues like '(#ID)' were seen in commit messages ### Commit Details @@ -441,40 +609,81 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
view details - * **Uncategorized** - - Release organize-rs_core v0.1.5, organize-rs v0.2.5 ([`6de8935`](https://github.com/organize-rs/organize/commit/6de893599162bbbb838c7c5f5fd3a81536cb9b30)) - - Implement `mimetype` filter ([`345d888`](https://github.com/organize-rs/organize/commit/345d8885d1ffe9bcfdc42c62fccbdc59a457ed0a)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.4, organize-rs v0.2.4 ([`b00bbe0`](https://github.com/organize-rs/organize/commit/b00bbe03cc7b50a08dcb2e6c98eb41a3a586f488)) - - Implement `last_accessed` and `last_modified` filters ([`4410c3f`](https://github.com/organize-rs/organize/commit/4410c3f8a45909c69a3fdca63ad8f6845b601dc3)) - - Implement `created` filter ([`f07ab6a`](https://github.com/organize-rs/organize/commit/f07ab6a4bd9be7674dad416f7b74e9b9196b3dca)) - - Remove human-panic dependency ([`9382256`](https://github.com/organize-rs/organize/commit/938225668c8879192a8e81a4872797907e3b4641)) - - Remove unused import ([`9f56d4c`](https://github.com/organize-rs/organize/commit/9f56d4ce1211abaf96f720c9874b1bba1915d755)) - - Cargo fix & cargo fmt ([`ee231a6`](https://github.com/organize-rs/organize/commit/ee231a69fcd825e6121c380f408c21ff2bf6c425)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.3, organize-rs v0.2.3 ([`c4d5428`](https://github.com/organize-rs/organize/commit/c4d5428c29ca7bf24746abf8ff45c74a4838159d)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.2, organize-rs v0.2.2 ([`2ebfdd7`](https://github.com/organize-rs/organize/commit/2ebfdd7fe2d386f54104f1a0a0d0230fd793f271)) - - Implement `empty` filter and global ignore for file names and directory paths ([`d51a81a`](https://github.com/organize-rs/organize/commit/d51a81a593cb37c54c0c91edfac60a5eb8de7c89)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.1, organize-rs v0.2.1 ([`1e0e2dc`](https://github.com/organize-rs/organize/commit/1e0e2dc36593da72422817d50eff61f13444ea32)) - - Update Versions ([`bb0cbce`](https://github.com/organize-rs/organize/commit/bb0cbce647d6f864100e81d1ac65a50fecb614c7)) - - Fix file_stem case insensitivity ([`03509fe`](https://github.com/organize-rs/organize/commit/03509fe7f6eedf5a94253a2e8e094c47ba114f69)) - - Add description to core lib ([`3b83e8e`](https://github.com/organize-rs/organize/commit/3b83e8ec10c2cbf24c7c35923aba9fc75687921c)) - - Update workspace manifests ([`d4eba0d`](https://github.com/organize-rs/organize/commit/d4eba0d0052f1d114bd7988edacfc5e53a62e4a9)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.0, organize-rs v0.2.0 ([`2dbdfe3`](https://github.com/organize-rs/organize/commit/2dbdfe3b7b2b656075901a013a587f6e6d4883cf)) - - Adjusting changelogs prior to release of organize-rs_core v0.1.0, organize-rs v0.2.0 ([`c008e22`](https://github.com/organize-rs/organize/commit/c008e22937993460a2997c33c13410625c25fc5b)) - - Add Changelogs ([`3b0ccbd`](https://github.com/organize-rs/organize/commit/3b0ccbda803b2a51cb5a33a03e8c9a382caeae96)) - - Update readme and version ([`287b420`](https://github.com/organize-rs/organize/commit/287b420a81ad730f79d90250c6fde2c7dda6f662)) - - Fix `name` filter `--ends_with` to include `file_stem` ([`af0efed`](https://github.com/organize-rs/organize/commit/af0efed87cabec1c5d7cf465452511008b2e475a)) - - Implement `name` filter ([`88587b0`](https://github.com/organize-rs/organize/commit/88587b09d28b0e1d8dfb9161086308edb19fbfbc)) - - Fix some grouping issues in Cli, create `cli` feature in organize-rs_core ([`b734e62`](https://github.com/organize-rs/organize/commit/b734e625d869163b07f63923414ffa900f93ca64)) - - Implement `filter_by_extension` ([`45e4d5b`](https://github.com/organize-rs/organize/commit/45e4d5b03185d5cd016d16795fdba0336c1496bd)) - - First try for implementing a file extension filter ([`45f2966`](https://github.com/organize-rs/organize/commit/45f296647ea46461ec89550f48eb22e07c037d5c)) - - Implement stub for filter methods ([`6c6f0f8`](https://github.com/organize-rs/organize/commit/6c6f0f89709a5f7b78ad8de3099ac3cbd6c5f6e3)) - - Add boilerplate for matching filters and actions ([`83c8cbc`](https://github.com/organize-rs/organize/commit/83c8cbc4dbf6e54b1941b8d1c5eabf399932845c)) - - Add czkawka_core dependency ([`5063aec`](https://github.com/organize-rs/organize/commit/5063aecdd41b99534d7c2539bcd60a5756401110)) - - Refine commands/subcommands ([`ed535f6`](https://github.com/organize-rs/organize/commit/ed535f68f92e4ec187a73fb628fcf4e86d1bda3e)) - - Rethink structure and change roadmap ([`f5df157`](https://github.com/organize-rs/organize/commit/f5df157a0ee6944c398450d5651965999ec7f1f3)) - - Add `actions` and `filters` as subcommands ([`60df64e`](https://github.com/organize-rs/organize/commit/60df64e3380870fb5182e9cd4f47bb792bc55ce7)) - - Start parsing config ([`0e36272`](https://github.com/organize-rs/organize/commit/0e36272f9e7db8e65daaad39d228d986ab952673)) - - Refactor to workspace and create new core library ([`0de540b`](https://github.com/organize-rs/organize/commit/0de540b0aa0ab07dc4f3b118e6f95b30312ea44e)) +- **Uncategorized** + - Release organize-rs_core v0.1.5, organize-rs v0.2.5 + ([`6de8935`](https://github.com/organize-rs/organize/commit/6de893599162bbbb838c7c5f5fd3a81536cb9b30)) + - Implement `mimetype` filter + ([`345d888`](https://github.com/organize-rs/organize/commit/345d8885d1ffe9bcfdc42c62fccbdc59a457ed0a)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.4, + organize-rs v0.2.4 + ([`b00bbe0`](https://github.com/organize-rs/organize/commit/b00bbe03cc7b50a08dcb2e6c98eb41a3a586f488)) + - Implement `last_accessed` and `last_modified` filters + ([`4410c3f`](https://github.com/organize-rs/organize/commit/4410c3f8a45909c69a3fdca63ad8f6845b601dc3)) + - Implement `created` filter + ([`f07ab6a`](https://github.com/organize-rs/organize/commit/f07ab6a4bd9be7674dad416f7b74e9b9196b3dca)) + - Remove human-panic dependency + ([`9382256`](https://github.com/organize-rs/organize/commit/938225668c8879192a8e81a4872797907e3b4641)) + - Remove unused import + ([`9f56d4c`](https://github.com/organize-rs/organize/commit/9f56d4ce1211abaf96f720c9874b1bba1915d755)) + - Cargo fix & cargo fmt + ([`ee231a6`](https://github.com/organize-rs/organize/commit/ee231a69fcd825e6121c380f408c21ff2bf6c425)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.3, + organize-rs v0.2.3 + ([`c4d5428`](https://github.com/organize-rs/organize/commit/c4d5428c29ca7bf24746abf8ff45c74a4838159d)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.2, + organize-rs v0.2.2 + ([`2ebfdd7`](https://github.com/organize-rs/organize/commit/2ebfdd7fe2d386f54104f1a0a0d0230fd793f271)) + - Implement `empty` filter and global ignore for file names and directory + paths + ([`d51a81a`](https://github.com/organize-rs/organize/commit/d51a81a593cb37c54c0c91edfac60a5eb8de7c89)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.1, + organize-rs v0.2.1 + ([`1e0e2dc`](https://github.com/organize-rs/organize/commit/1e0e2dc36593da72422817d50eff61f13444ea32)) + - Update Versions + ([`bb0cbce`](https://github.com/organize-rs/organize/commit/bb0cbce647d6f864100e81d1ac65a50fecb614c7)) + - Fix file_stem case insensitivity + ([`03509fe`](https://github.com/organize-rs/organize/commit/03509fe7f6eedf5a94253a2e8e094c47ba114f69)) + - Add description to core lib + ([`3b83e8e`](https://github.com/organize-rs/organize/commit/3b83e8ec10c2cbf24c7c35923aba9fc75687921c)) + - Update workspace manifests + ([`d4eba0d`](https://github.com/organize-rs/organize/commit/d4eba0d0052f1d114bd7988edacfc5e53a62e4a9)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.0, + organize-rs v0.2.0 + ([`2dbdfe3`](https://github.com/organize-rs/organize/commit/2dbdfe3b7b2b656075901a013a587f6e6d4883cf)) + - Adjusting changelogs prior to release of organize-rs_core v0.1.0, + organize-rs v0.2.0 + ([`c008e22`](https://github.com/organize-rs/organize/commit/c008e22937993460a2997c33c13410625c25fc5b)) + - Add Changelogs + ([`3b0ccbd`](https://github.com/organize-rs/organize/commit/3b0ccbda803b2a51cb5a33a03e8c9a382caeae96)) + - Update readme and version + ([`287b420`](https://github.com/organize-rs/organize/commit/287b420a81ad730f79d90250c6fde2c7dda6f662)) + - Fix `name` filter `--ends_with` to include `file_stem` + ([`af0efed`](https://github.com/organize-rs/organize/commit/af0efed87cabec1c5d7cf465452511008b2e475a)) + - Implement `name` filter + ([`88587b0`](https://github.com/organize-rs/organize/commit/88587b09d28b0e1d8dfb9161086308edb19fbfbc)) + - Fix some grouping issues in Cli, create `cli` feature in organize-rs_core + ([`b734e62`](https://github.com/organize-rs/organize/commit/b734e625d869163b07f63923414ffa900f93ca64)) + - Implement `filter_by_extension` + ([`45e4d5b`](https://github.com/organize-rs/organize/commit/45e4d5b03185d5cd016d16795fdba0336c1496bd)) + - First try for implementing a file extension filter + ([`45f2966`](https://github.com/organize-rs/organize/commit/45f296647ea46461ec89550f48eb22e07c037d5c)) + - Implement stub for filter methods + ([`6c6f0f8`](https://github.com/organize-rs/organize/commit/6c6f0f89709a5f7b78ad8de3099ac3cbd6c5f6e3)) + - Add boilerplate for matching filters and actions + ([`83c8cbc`](https://github.com/organize-rs/organize/commit/83c8cbc4dbf6e54b1941b8d1c5eabf399932845c)) + - Add czkawka_core dependency + ([`5063aec`](https://github.com/organize-rs/organize/commit/5063aecdd41b99534d7c2539bcd60a5756401110)) + - Refine commands/subcommands + ([`ed535f6`](https://github.com/organize-rs/organize/commit/ed535f68f92e4ec187a73fb628fcf4e86d1bda3e)) + - Rethink structure and change roadmap + ([`f5df157`](https://github.com/organize-rs/organize/commit/f5df157a0ee6944c398450d5651965999ec7f1f3)) + - Add `actions` and `filters` as subcommands + ([`60df64e`](https://github.com/organize-rs/organize/commit/60df64e3380870fb5182e9cd4f47bb792bc55ce7)) + - Start parsing config + ([`0e36272`](https://github.com/organize-rs/organize/commit/0e36272f9e7db8e65daaad39d228d986ab952673)) + - Refactor to workspace and create new core library + ([`0de540b`](https://github.com/organize-rs/organize/commit/0de540b0aa0ab07dc4f3b118e6f95b30312ea44e)) +
## 0.1.4 (2023-05-20) @@ -486,4 +695,3 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## 0.1.1 (2023-05-18) ## 0.1.0 (2023-05-18) - diff --git a/crates/organize-rs_core/Cargo.toml b/crates/organize-rs_core/Cargo.toml index a24dc34..a5e9b7a 100644 --- a/crates/organize-rs_core/Cargo.toml +++ b/crates/organize-rs_core/Cargo.toml @@ -1,15 +1,15 @@ [package] name = "organize-rs_core" version = "0.2.3" -edition = { workspace = true } authors = { workspace = true } -repository = { workspace = true } -license = { workspace = true } categories = { workspace = true } -keywords = { workspace = true } -description = { workspace = true } +edition = { workspace = true } # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html include = ["src/**/*", "LICENSE", "CHANGELOG.md", "config/config_template.yaml"] +keywords = { workspace = true } +license = { workspace = true } +repository = { workspace = true } +description = { workspace = true } # [[example]] # name = "templating_parser" @@ -26,44 +26,43 @@ cli = ["dep:clap", "dep:indicatif"] [dependencies] # czkawka_core = { workspace = true } -infer = { workspace = true } -mime = { workspace = true } +aho-corasick = { workspace = true } +byte-unit = { workspace = true } +chrono = { workspace = true } clap = { workspace = true, optional = true } +console = { workspace = true } +crossbeam = { workspace = true } directories = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } -thiserror = { workspace = true } -once_cell = { workspace = true } +displaydoc = { workspace = true } duct = { workspace = true } -chrono = { workspace = true } -regex = { workspace = true } +filetime = { workspace = true } indicatif = { workspace = true, optional = true } -jwalk = { workspace = true } -serde_yaml = { workspace = true } -toml = { workspace = true } +infer = { workspace = true } itertools = { workspace = true } -displaydoc = { workspace = true } -winnow = { workspace = true } -byte-unit = { workspace = true } -trash = { workspace = true } +jwalk = { workspace = true } +mime = { workspace = true } +once_cell = { workspace = true } rayon = { workspace = true } -filetime = { workspace = true } -crossbeam = { workspace = true } +regex = { workspace = true } ron = { workspace = true } -serde_with = { workspace = true } -console = { workspace = true } semver = { workspace = true } -aho-corasick = { workspace = true } - +serde = { workspace = true } +serde_json = { workspace = true } +serde_with = { workspace = true } +serde_yaml = { workspace = true } +thiserror = { workspace = true } +toml = { workspace = true } +trash = { workspace = true } +winnow = { workspace = true } [dev-dependencies] -rustup-toolchain = "0.1.5" -rustdoc-json = "0.8.6" -public-api = "0.31.0" expect-test = "1.4.1" -rstest = { workspace = true } -quickcheck = { workspace = true } -quickcheck_macros = { workspace = true } -pretty_assertions = { workspace = true } insta = { workspace = true } organize-rs_testing = { workspace = true } +pretty_assertions = { workspace = true } +public-api = "0.31.0" +quickcheck = { workspace = true } +quickcheck_macros = { workspace = true } +rstest = { workspace = true } +rustdoc-json = "0.8.6" +rustup-toolchain = "0.1.5" diff --git a/crates/organize-rs_testing/Cargo.toml b/crates/organize-rs_testing/Cargo.toml index 7e03613..1ed8d92 100644 --- a/crates/organize-rs_testing/Cargo.toml +++ b/crates/organize-rs_testing/Cargo.toml @@ -1,12 +1,12 @@ [package] -publish = false name = "organize-rs_testing" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -tempfile = { workspace = true } aho-corasick = { workspace = true } jwalk = { workspace = true } +tempfile = { workspace = true } diff --git a/docs/DSL.md b/docs/DSL.md index 2e32b20..1a17af2 100644 --- a/docs/DSL.md +++ b/docs/DSL.md @@ -6,27 +6,35 @@ ## Goals -- make it possible to create a general model, as in I have a directory where all of the files/folders go, that match these filters +- make it possible to create a general model, as in I have a directory where all + of the files/folders go, that match these filters - so basically the other way around, not `filter->action` but - `destination <- action(action_command) <- filter(filter_cmd) <- location` + `destination <- action(action_command) <- filter(filter_cmd) <- location` - as in: + as in: - `app_directory <- action[move] <- filter[(ext(apps) | !name(keep_)) & size(20mb..)] <- ~/Downloads` + `app_directory <- action[move] <- filter[(ext(apps) | !name(keep_)) & size(20mb..)] <- ~/Downloads` -- make it simple and straight forward to invoke filters and defining the actions that are applied to them +- make it simple and straight forward to invoke filters and defining the actions + that are applied to them -- make it possible to chain filters, so you can run multiple filters on the same defined location twice or more - - e.g. when you want to filter for `zip` files that have been created in the last seven days - - negate filters, so you can easily filter for `zip` files that have **not** been created in the last seven days +- make it possible to chain filters, so you can run multiple filters on the same + defined location twice or more + - e.g. when you want to filter for `zip` files that have been created in the + last seven days + - negate filters, so you can easily filter for `zip` files that have **not** + been created in the last seven days -- make it possible to pass optional arguments easily, especially the often used ones like `recursive` (bool) and `max-depth` (u64) +- make it possible to pass optional arguments easily, especially the often used + ones like `recursive` (bool) and `max-depth` (u64) - make it possible to create aliases for a group of certain directories - so you can keep it DRY and access them whereever you need - error handling can be tricky here: - - e.g. filtering for files and moving them to an alias that contains more than one location + - e.g. filtering for files and moving them to an alias that contains more + than one location - [RESEARCH] what should happen in that case? - we shouldn't just move them to the first given location - - we shouldn't just copy them to each location and then remove the original + - we shouldn't just copy them to each location and then remove the + original diff --git a/dprint.json b/dprint.json new file mode 100644 index 0000000..7ebe2d4 --- /dev/null +++ b/dprint.json @@ -0,0 +1,30 @@ +{ + "lineWidth": 80, + "markdown": { + "lineWidth": 80, + "emphasisKind": "asterisks", + "strongKind": "asterisks", + "textWrap": "always" + }, + "toml": { + "lineWidth": 80 + }, + "json": { + "lineWidth": 80, + "indentWidth": 4 + }, + "includes": [ + "**/*.{md}", + "**/*.{toml}", + "**/*.{json}" + ], + "excludes": [ + "target/**/*", + "**/*-lock.json" + ], + "plugins": [ + "https://plugins.dprint.dev/markdown-0.16.1.wasm", + "https://plugins.dprint.dev/toml-0.5.4.wasm", + "https://plugins.dprint.dev/json-0.17.4.wasm" + ] +} diff --git a/renovate.json b/renovate.json index 39a2b6e..f9c2c32 100644 --- a/renovate.json +++ b/renovate.json @@ -1,6 +1,6 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "extends": [ - "config:base" - ] + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:base" + ] } diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml index 523c088..4074b5d 100644 --- a/xtask/Cargo.toml +++ b/xtask/Cargo.toml @@ -1,8 +1,8 @@ [package] -publish = false name = "xtask" version = "0.1.0" edition = "2021" +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 274f06913a25bc23df30b72b707c619bf1b51de0 Mon Sep 17 00:00:00 2001 From: simonsan <14062932+simonsan@users.noreply.github.com> Date: Sat, 9 Sep 2023 05:31:55 +0200 Subject: [PATCH 12/12] style: fmt Signed-off-by: simonsan <14062932+simonsan@users.noreply.github.com> --- crates/organize-rs_core/src/actions.rs | 1 - .../src/actors/filter_applicator.rs | 10 +++---- crates/organize-rs_core/src/filters/impl_.rs | 18 ++++++------- .../src/parsers/period_range.rs | 5 ++-- .../src/parsers/size_range.rs | 11 ++++---- crates/organize-rs_core/src/templating.rs | 27 ++++++++----------- justfile | 5 +++- src/commands/filter.rs | 2 +- 8 files changed, 35 insertions(+), 44 deletions(-) diff --git a/crates/organize-rs_core/src/actions.rs b/crates/organize-rs_core/src/actions.rs index 00f5e9f..2bf7470 100644 --- a/crates/organize-rs_core/src/actions.rs +++ b/crates/organize-rs_core/src/actions.rs @@ -185,7 +185,6 @@ impl Default for MacOsTagColourKind { } } - /// Mode how should be written to a file #[cfg_attr(feature = "cli", derive(ValueEnum))] #[derive(Debug, Clone, Deserialize, Serialize)] diff --git a/crates/organize-rs_core/src/actors/filter_applicator.rs b/crates/organize-rs_core/src/actors/filter_applicator.rs index f5e776f..45fc12f 100644 --- a/crates/organize-rs_core/src/actors/filter_applicator.rs +++ b/crates/organize-rs_core/src/actors/filter_applicator.rs @@ -1,13 +1,9 @@ use crate::{actors::location_walker::DirEntryData, filters::FilterGroupCollection}; use itertools::{Either, Itertools}; - - -use crate::{ - filters::{ - FilterApplicationKind, FilterFilterClosureSliceMut, FilterGroup, FilterGroupOperationKind, - FilterKind, - }, +use crate::filters::{ + FilterApplicationKind, FilterFilterClosureSliceMut, FilterGroup, FilterGroupOperationKind, + FilterKind, }; #[derive(Debug, Default)] diff --git a/crates/organize-rs_core/src/filters/impl_.rs b/crates/organize-rs_core/src/filters/impl_.rs index c37bcf8..fc260d9 100644 --- a/crates/organize-rs_core/src/filters/impl_.rs +++ b/crates/organize-rs_core/src/filters/impl_.rs @@ -99,10 +99,10 @@ impl FilterKind { let Some(Some(file_stem)) = entry .path() .file_stem() - .map(|f|{ - f.to_str().map(|f| f.to_owned())}) else { - return false - }; + .map(|f| f.to_str().map(|f| f.to_owned())) + else { + return false; + }; let file_stem = make_lowercase_if(file_stem); @@ -245,9 +245,7 @@ impl FilterKind { } pub(crate) fn matches_date(item_date_secs: i64, range: &Option) -> bool { - let Some(range) = range else { - return false - }; + let Some(range) = range else { return false }; let now = FileTime::now(); let seconds_since_created = match u64::try_from(now.seconds() - item_date_secs) { @@ -269,7 +267,9 @@ impl FilterKind { mimetype: &'args [String], ) -> Box) -> bool + 'args> { Box::new(|entry| { - let Ok(Some(file_kind)) = infer::get_from_path(entry.path()) else { return false }; + let Ok(Some(file_kind)) = infer::get_from_path(entry.path()) else { + return false; + }; let file_mime_type = match file_kind.mime_type().parse::() { Ok(it) => it, @@ -296,7 +296,7 @@ impl FilterKind { ) -> Box) -> bool + 'args> { Box::new(|entry| { let Some(range) = range.clone() else { - return false + return false; }; entry .metadata() diff --git a/crates/organize-rs_core/src/parsers/period_range.rs b/crates/organize-rs_core/src/parsers/period_range.rs index 69b5daa..71a06f4 100644 --- a/crates/organize-rs_core/src/parsers/period_range.rs +++ b/crates/organize-rs_core/src/parsers/period_range.rs @@ -5,11 +5,10 @@ use std::{fmt::Display, ops::Range, str::FromStr}; use serde::Serialize; use serde_with::DeserializeFromStr; use winnow::{ - error::{Error, ErrorKind}, Parser, + error::{Error, ErrorKind}, + Parser, }; - - use crate::{ filters::DateUnitKind, parsers::{ diff --git a/crates/organize-rs_core/src/parsers/size_range.rs b/crates/organize-rs_core/src/parsers/size_range.rs index 3285419..aefa0f5 100644 --- a/crates/organize-rs_core/src/parsers/size_range.rs +++ b/crates/organize-rs_core/src/parsers/size_range.rs @@ -5,16 +5,15 @@ use std::{fmt::Display, ops::Range, str::FromStr}; use serde::Serialize; use serde_with::DeserializeFromStr; use winnow::{ - error::{Error, ErrorKind}, Parser, + error::{Error, ErrorKind}, + Parser, }; use byte_unit::Byte; -use crate::{ - parsers::{ - parse_left_range_boundary, parse_right_range_boundary, parse_whole_range, Condition, - RangeBoundarySide, SingleRangeCondition, - }, +use crate::parsers::{ + parse_left_range_boundary, parse_right_range_boundary, parse_whole_range, Condition, + RangeBoundarySide, SingleRangeCondition, }; #[derive(Debug, Clone, PartialEq, DeserializeFromStr, Serialize)] diff --git a/crates/organize-rs_core/src/templating.rs b/crates/organize-rs_core/src/templating.rs index ef8d23e..4ca5a0f 100644 --- a/crates/organize-rs_core/src/templating.rs +++ b/crates/organize-rs_core/src/templating.rs @@ -1,4 +1,4 @@ -use std::{str::FromStr, collections::HashMap}; +use std::{collections::HashMap, str::FromStr}; use winnow::error::Error; @@ -22,7 +22,6 @@ use once_cell::sync::Lazy; // m // }); - #[non_exhaustive] #[derive(Debug, Clone, PartialEq, Eq)] pub enum TransformationKind { @@ -157,7 +156,7 @@ pub enum TemplateKind { kind: TransformationKind, data: TemplateFeatureKind, }, - Uninitialized + Uninitialized, } impl TemplateKind { @@ -216,12 +215,7 @@ impl FromStr for TemplateKind { #[cfg(test)] mod tests { - use std::{ - borrow::{Borrow}, - ffi::OsStr, - path::{PathBuf}, - str::FromStr, - }; + use std::{borrow::Borrow, ffi::OsStr, path::PathBuf, str::FromStr}; use itertools::Itertools; @@ -229,7 +223,8 @@ mod tests { #[test] fn test_aho_corasick_template_passes() { - let example = PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(metadata.extension)}}\"#); + let example = + PathBuf::from(r#"C:\Users\dailyuse\Desktop\{{uppercase(metadata.extension)}}\"#); let patterns = &["metadata.extension"]; let ac = AhoCorasick::new(patterns).unwrap(); @@ -239,8 +234,7 @@ mod tests { let path = ac.replace_all(example.to_str().unwrap(), replace_with); dbg!(&path); - assert_eq!(path, r#"C:\Users\dailyuse\Desktop\{ uppercase(toml) }\"# ) - + assert_eq!(path, r#"C:\Users\dailyuse\Desktop\{ uppercase(toml) }\"#) } #[test] @@ -256,10 +250,11 @@ mod tests { .iter() .enumerate() .map(|(idx, component)| { - let Ok(template) = TemplateKind::from_str(component.to_string_lossy().borrow()) else { - return component - }; - + let Ok(template) = TemplateKind::from_str(component.to_string_lossy().borrow()) + else { + return component; + }; + assert_eq!( template, TemplateKind::Transformation { diff --git a/justfile b/justfile index ef13594..910c5e8 100644 --- a/justfile +++ b/justfile @@ -130,4 +130,7 @@ pa: # Run a sample script run: - cargo run -- run-script --path C:\Users\dailyuse\dev-src\organize\scripts\test.rhai \ No newline at end of file + cargo run -- run-script --path C:\Users\dailyuse\dev-src\organize\scripts\test.rhai + +fmt: format + dprint fmt \ No newline at end of file diff --git a/src/commands/filter.rs b/src/commands/filter.rs index cd86579..a996b49 100644 --- a/src/commands/filter.rs +++ b/src/commands/filter.rs @@ -10,7 +10,7 @@ use organize_rs_core::{ FilterApplicationKind, FilterGroup, FilterGroupCollection, FilterGroupOperationKind, FilterKind, RecursiveFilterArgs, }, - locations::{TargetKind}, + locations::TargetKind, }; /// `filter` subcommand