diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 34f9676..8a7bbec 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -89,7 +89,7 @@ jobs: if: matrix.build != 'aarch64-linux' run: cargo build - - uses: actions/upload-artifact@v4.6.0 + - uses: actions/upload-artifact@v4.6.2 with: name: bins-${{ matrix.build }} # Two paths, the first for x86_64 jobs, the second for aarch64. diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 40dff69..1b016a7 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -77,7 +77,7 @@ jobs: cp "target/${{ matrix.target }}/release/$BIN_NAME" "dist/" fi - - uses: actions/upload-artifact@v4.6.0 + - uses: actions/upload-artifact@v4.6.2 with: name: cotp-${{ matrix.build }} path: dist diff --git a/Cargo.lock b/Cargo.lock index 78364c4..2f620f4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,6 +64,15 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + [[package]] name = "allocator-api2" version = "0.2.16" @@ -208,6 +217,16 @@ dependencies = [ "generic-array", ] +[[package]] +name = "bstr" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "531a9155a481e2ee699d4f98f43c0ca4ff8ee1bfd55c31e9e98fb29d2b176fe0" +dependencies = [ + "memchr", + "serde", +] + [[package]] name = "bytemuck" version = "1.12.1" @@ -287,9 +306,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.29" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184" +checksum = "d8aa86934b44c19c50f87cc2790e19f54f7a67aedb64101c2e1a2e5ecfb73944" dependencies = [ "clap_builder", "clap_derive", @@ -297,9 +316,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.29" +version = "4.5.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9" +checksum = "2414dbb2dd0695280da6ea9261e327479e9d37b0630f6b53ba2a11c60c679fd9" dependencies = [ "anstream", "anstyle", @@ -309,9 +328,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.28" +version = "4.5.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed" +checksum = "09176aae279615badda0765c0c0b3f6ed53f4709118af73cf4655d85d1530cd7" dependencies = [ "heck", "proc-macro2", @@ -435,7 +454,8 @@ dependencies = [ "derive_builder", "dirs", "enum_dispatch", - "getrandom 0.3.1", + "getrandom 0.3.2", + "globset", "hex", "hmac", "md-5", @@ -611,6 +631,17 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "dlib" version = "0.5.0" @@ -712,14 +743,14 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" dependencies = [ "cfg-if", "libc", - "wasi 0.13.3+wasi-0.2.2", - "windows-targets 0.52.0", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", ] [[package]] @@ -738,6 +769,19 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" +[[package]] +name = "globset" +version = "0.4.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54a1028dfc5f5df5da8a56a73e6c153c9a9708ec57232470703592a3f18e49f5" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "hashbrown" version = "0.14.2" @@ -775,6 +819,124 @@ dependencies = [ "digest", ] +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7515e6d781098bf9f7205ab3fc7e9709d34554ae0b21ddbcb5febfa4bc7df11d" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e8338228bdc8ab83303f16b797e177953730f601a96c25d10cb3ab0daa0cb7" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85fb8799753b75aee8d2a21d7c14d9f38921b54b3dbda10f5a3c7a7b82dba5e2" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "ident_case" version = "1.0.1" @@ -783,12 +945,23 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] @@ -897,6 +1070,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "litemap" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" + [[package]] name = "lock_api" version = "0.4.9" @@ -909,12 +1088,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.17" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] name = "lru" @@ -946,9 +1122,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.6.3" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f232d6ef707e1956a43342693d2a31e72989554d58299d7a88738cc95b0d35c" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" @@ -1216,6 +1392,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "rand_core" version = "0.6.4" @@ -1267,6 +1449,23 @@ dependencies = [ "thiserror", ] +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + [[package]] name = "rpassword" version = "7.3.1" @@ -1365,18 +1564,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -1385,9 +1584,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", "memchr", @@ -1458,9 +1657,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.10.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smithay-client-toolkit" @@ -1490,6 +1689,12 @@ dependencies = [ "wayland-client", ] +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + [[package]] name = "static_assertions" version = "1.1.0" @@ -1558,6 +1763,17 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] + [[package]] name = "thiserror" version = "1.0.37" @@ -1607,20 +1823,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7300fbefb4dadc1af235a9cef3737cea692a9d97e1b9cbcd4ebdae6f8868e6fb" [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" dependencies = [ - "tinyvec_macros", + "displaydoc", + "zerovec", ] -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - [[package]] name = "tracing" version = "0.1.37" @@ -1669,27 +1880,12 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" -[[package]] -name = "unicode-bidi" -version = "0.3.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" - [[package]] name = "unicode-ident" version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3" -[[package]] -name = "unicode-normalization" -version = "0.1.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.10.0" @@ -1730,9 +1926,9 @@ dependencies = [ [[package]] name = "url" -version = "2.5.2" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -1745,6 +1941,18 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + [[package]] name = "utf8parse" version = "0.2.1" @@ -1771,9 +1979,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] name = "wasi" -version = "0.13.3+wasi-0.2.2" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" dependencies = [ "wit-bindgen-rt", ] @@ -2084,13 +2292,25 @@ checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" [[package]] name = "wit-bindgen-rt" -version = "0.33.0" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "write16" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" + +[[package]] +name = "writeable" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" + [[package]] name = "x11-clipboard" version = "0.7.0" @@ -2137,6 +2357,30 @@ version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52839dc911083a8ef63efa4d039d1f58b5e409f923e44c80828f206f66e5541c" +[[package]] +name = "yoke" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "synstructure", +] + [[package]] name = "zerocopy" version = "0.7.31" @@ -2157,6 +2401,27 @@ dependencies = [ "syn 2.0.82", ] +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", + "synstructure", +] + [[package]] name = "zeroize" version = "1.8.1" @@ -2176,3 +2441,25 @@ dependencies = [ "quote", "syn 2.0.82", ] + +[[package]] +name = "zerovec" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.82", +] diff --git a/Cargo.toml b/Cargo.toml index 5d63dc9..263554c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,19 +27,19 @@ debug = false strip = "symbols" [dependencies] -serde = { version = "1.0.217", features = ["derive"] } -serde_json = "1.0.138" +serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" dirs = "5.0.1" rpassword = "7.3.1" data-encoding = "2.8.0" copypasta-ext = "0.4.4" zeroize = { version = "1.8.1", features = ["zeroize_derive"] } -clap = { version = "4.5.29", features = ["derive"] } +clap = { version = "4.5.35", features = ["derive"] } hmac = "0.12.1" sha1 = "0.10.6" sha2 = "0.10.8" chacha20poly1305 = "0.10.1" -getrandom = "0.3.1" +getrandom = "0.3.2" rust-argon2 = "2.1.0" scrypt = "0.11.0" aes-gcm = "0.10.3" @@ -50,7 +50,8 @@ base64 = "0.22.1" md-5 = "0.10.6" ratatui = { version = "0.29.0", features = ["all-widgets"] } crossterm = "0.28.1" -url = "2.5.2" +url = "2.5.4" color-eyre = "0.6.3" enum_dispatch = "0.3.13" derive_builder = "0.20.1" +globset = "0.4.16" diff --git a/src/arguments/add.rs b/src/arguments/add.rs index 62c8620..0e3b31e 100644 --- a/src/arguments/add.rs +++ b/src/arguments/add.rs @@ -80,7 +80,7 @@ impl SubcommandExecutor for AddArgs { fn get_from_args(matches: AddArgs) -> color_eyre::Result { let secret = rpassword::prompt_password("Insert the secret: ").map_err(ErrReport::from)?; - map_args_to_code(secret, matches).map_err(ErrReport::from) + map_args_to_code(secret, matches) } fn map_args_to_code(secret: String, matches: AddArgs) -> Result { diff --git a/src/arguments/extract.rs b/src/arguments/extract.rs index 63728e3..8722c2f 100644 --- a/src/arguments/extract.rs +++ b/src/arguments/extract.rs @@ -2,10 +2,11 @@ use crate::otp::otp_element::OTPDatabase; use crate::{clipboard, otp::otp_element::OTPElement}; use clap::Args; use color_eyre::eyre::eyre; +use globset::{GlobBuilder, GlobMatcher}; use super::SubcommandExecutor; -#[derive(Args)] +#[derive(Args, Default)] pub struct ExtractArgs { /// Code Index #[arg(short, long, required_unless_present_any = ["issuer", "label"])] @@ -24,19 +25,57 @@ pub struct ExtractArgs { pub copy_to_clipboard: bool, } +// Contains glob filters for each field we can filter on +struct ExtractFilterGlob { + issuer_glob: Option, + label_glob: Option, + index: Option, +} + +impl TryFrom for ExtractFilterGlob { + type Error = color_eyre::eyre::ErrReport; + + fn try_from(value: ExtractArgs) -> Result { + let issuer_glob = if let Some(issuer) = value.issuer { + Some(create_matcher(&issuer)?) + } else { + None + }; + + let label_glob = if let Some(label) = value.label { + Some(create_matcher(&label)?) + } else { + None + }; + + Ok(Self { + issuer_glob, + label_glob, + index: value.index, + }) + } +} + +fn create_matcher( + glob: &str, +) -> Result>::Error> { + Ok(GlobBuilder::new(glob) + .case_insensitive(true) + .build()? + .compile_matcher()) +} + impl SubcommandExecutor for ExtractArgs { fn run_command(self, otp_database: OTPDatabase) -> color_eyre::Result { - let first_with_filters = otp_database - .elements - .iter() - .enumerate() - .find(|(index, code)| filter_extract(&self, *index, code)) - .map(|(_, code)| code); + let copy_to_clipboard = self.copy_to_clipboard; + let globbed: ExtractFilterGlob = self.try_into()?; + + let first_with_filters = find_match(&otp_database, globbed); if let Some(otp) = first_with_filters { let code = otp.get_otp_code()?; println!("{code}"); - if self.copy_to_clipboard { + if copy_to_clipboard { let _ = clipboard::copy_string_to_clipboard(code.as_str())?; println!("Copied to clipboard"); } @@ -47,16 +86,244 @@ impl SubcommandExecutor for ExtractArgs { } } -fn filter_extract(args: &ExtractArgs, index: usize, code: &OTPElement) -> bool { - let match_by_index = args.index.map_or(true, |i| i == index); +fn find_match(otp_database: &OTPDatabase, globbed: ExtractFilterGlob) -> Option<&OTPElement> { + otp_database + .elements + .iter() + .enumerate() + .find(|(index, code)| filter_extract(&globbed, *index, code)) + .map(|(_, code)| code) +} + +fn filter_extract(args: &ExtractFilterGlob, index: usize, candidate: &OTPElement) -> bool { + let match_by_index = args.index.is_none_or(|i| i == index); - let match_by_issuer = args.issuer.as_ref().map_or(true, |issuer| { - code.issuer.to_lowercase() == issuer.to_lowercase() - }); + let match_by_issuer = args + .issuer_glob + .as_ref() + .is_none_or(|issuer| issuer.is_match(&candidate.issuer)); - let match_by_label = args.label.as_ref().map_or(true, |label| { - code.label.to_lowercase() == label.to_lowercase() - }); + let match_by_label = args + .label_glob + .as_ref() + .is_none_or(|label| label.is_match(&candidate.label)); match_by_index && match_by_issuer && match_by_label } + +#[cfg(test)] +mod tests { + + use crate::{ + arguments::extract::ExtractArgs, + otp::otp_element::{OTPDatabase, OTPElementBuilder}, + }; + + use super::find_match; + + #[test] + fn test_glob_filtering_good_issuer() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + issuer: Some("test-iss*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_some()); + } + + #[test] + fn test_glob_filtering_good_label() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + label: Some("test-la*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_some()); + } + + #[test] + fn test_glob_filtering_no_match() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + label: Some("test-lala*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_none()); + } + + #[test] + fn test_glob_filtering_multiple_filters_match() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + issuer: Some("test*".to_string()), + label: Some("test-la*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_some()); + } + + #[test] + fn test_glob_filtering_multiple_filters_no_match() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + issuer: Some("test-no*".to_string()), + label: Some("test-la*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_none()); + } + + #[test] + fn test_glob_filtering_case_insensitive() { + // Arrange + let mut otp_database = OTPDatabase::default(); + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer") + .label("test-label") + .secret("AA") + .build() + .unwrap(), + ); + + otp_database.add_element( + OTPElementBuilder::default() + .issuer("test-issuer2") + .label("test-label2") + .secret("AA") + .build() + .unwrap(), + ); + + let filter = ExtractArgs { + issuer: Some("TeSt-iSS*".to_string()), + ..Default::default() + }; + + // Act + let found_match = find_match(&otp_database, filter.try_into().unwrap()); + + // Assert + assert!(found_match.is_some()); + } +} diff --git a/src/arguments/passwd.rs b/src/arguments/passwd.rs index 82a7f36..1ca741f 100644 --- a/src/arguments/passwd.rs +++ b/src/arguments/passwd.rs @@ -1,5 +1,4 @@ use clap::Args; -use color_eyre::eyre::ErrReport; use zeroize::Zeroize; use crate::{otp::otp_element::OTPDatabase, utils}; @@ -12,9 +11,7 @@ pub struct PasswdArgs; impl SubcommandExecutor for PasswdArgs { fn run_command(self, mut database: OTPDatabase) -> color_eyre::Result { let mut new_password = utils::verified_password("New password: ", 8); - database - .save_with_pw(&new_password) - .map_err(ErrReport::from)?; + database.save_with_pw(&new_password)?; new_password.zeroize(); Ok(database) } diff --git a/src/path.rs b/src/path.rs index 74832cd..5bae99a 100644 --- a/src/path.rs +++ b/src/path.rs @@ -36,7 +36,6 @@ fn get_default_db_path() -> PathBuf { let home_path = home_dir().map(|path| path.join(HOME_PATH)); data_dir() - .map(PathBuf::from) .map(|p| p.join(XDG_PATH)) .inspect(|xdg| { if !xdg.exists() {