diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index dbbcc5b5..32519537 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -81,67 +81,46 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Install minimal toolchain - uses: actions-rs/toolchain@v1 + # Install minimal toolchain + - name: Install minimal stable toolchain + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - profile: minimal components: clippy - uses: Swatinem/rust-cache@v2 - name: Run clippy - uses: actions-rs/cargo@v1 - with: - command: clippy - args: -- -D warnings + run: cargo clippy -- -D warnings docs: - env: - SQLX_OFFLINE: true + name: Documentation runs-on: ubuntu-latest + env: + SQLX_OFFLINE: true + RUSTDOCFLAGS: -Dwarnings steps: - uses: actions/checkout@v4 - - - name: Install minimal toolchain - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - override: true - profile: minimal - + - uses: dtolnay/rust-toolchain@nightly - uses: Swatinem/rust-cache@v2 - - - name: Run rustdoc - env: - RUSTDOCFLAGS: -D warnings - uses: actions-rs/cargo@v1 - with: - command: doc - args: --no-deps --document-private-items + - uses: dtolnay/install@cargo-docs-rs + - run: cargo doc --no-deps --document-private-items fmt: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Install minimal toolchain - uses: actions-rs/toolchain@v1 + # Install minimal toolchain + - name: Install minimal stable toolchain + uses: dtolnay/rust-toolchain@stable with: - toolchain: stable - override: true - profile: minimal components: rustfmt - - uses: Swatinem/rust-cache@v2 - - name: Run rustfmt - uses: actions-rs/cargo@v1 - with: - command: fmt - args: --all --check + run: cargo fmt --all --check + # This is just a curtesy check to tell whether it should be breaking changes or not + # If CI fail because of this, just bump the version msrv: env: SQLX_OFFLINE: true @@ -149,5 +128,12 @@ jobs: steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 - - uses: taiki-e/install-action@cargo-hack - - run: cargo hack check --rust-version --workspace --all-targets --ignore-private \ No newline at end of file + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install cargo-msrv + run: cargo install cargo-msrv --all-features + - name: Run cargo-msrv + run: cargo msrv verify --output-format json + - name: Run cargo-msrv on verification failure + if: ${{ failure() }} + run: cargo msrv find --output-format json diff --git a/CHANGELOG.md b/CHANGELOG.md index 7907703e..4f5fba00 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,24 @@ All notable changes to this project will be documented in this file. +## [0.4.2] - 2024-12-20 + +### 🚀 Features + +- Wrong Mapping finder +- Add lint filter +- *(lint)* Missing_remixer_rel +- Lint severity colors +- *(lint)* Missing_remix_rel +- *(lint)* Soundtrack_without_disambiguation +- Add listening time option on radio overdue +- *(lint)* Suspicious_remix + +### ⚙️ Miscellaneous Tasks + +- Release v0.4.1 +- Remove actionrs + ## [0.4.0] - 2024-12-11 ### 🚀 Features diff --git a/Cargo.lock b/Cargo.lock index 88f1eba2..e991fc28 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -68,7 +68,7 @@ dependencies = [ [[package]] name = "alistral" -version = "0.4.1" +version = "0.4.2" dependencies = [ "async-fn-stream", "chrono", @@ -103,7 +103,9 @@ dependencies = [ "serde_json", "serial_test", "sqlx", - "thiserror 2.0.6", + "strsim", + "strum_macros", + "thiserror 2.0.8", "tokio", "tracing", "tracing-subscriber", @@ -112,9 +114,9 @@ dependencies = [ [[package]] name = "allocator-api2" -version = "0.2.20" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45862d1c77f2228b9e10bc609d5bc203d86ebc9b87ad8d5d5167a6c9abf739d9" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -213,7 +215,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -233,9 +235,9 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "attohttpc" -version = "0.28.0" +version = "0.28.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a13149d0cf3f7f9b9261fad4ec63b2efbf9a80665f52def86282d26255e6331" +checksum = "412b79ce053cef36eda52c25664b45ec92a21769488e20d5a8bf0b3c9e1a28cb" dependencies = [ "flate2", "http", @@ -335,7 +337,7 @@ dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -401,9 +403,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.2" +version = "1.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f34d93e62b03caf570cccc334cbc6c2fceca82f39211051345108adcba3eebdc" +checksum = "9157bbaa6b165880c27a4293a474c91cdcf265cc68cc829bf10be0964a391caf" dependencies = [ "jobserver", "libc", @@ -483,9 +485,9 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.5.38" +version = "4.5.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01" +checksum = "ac2e663e3e3bed2d32d065a8404024dad306e699a04263ec59919529f803aee9" dependencies = [ "clap", ] @@ -499,7 +501,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -552,15 +554,15 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "ea3c6ecd8059b57859df5c69830340ed3c41d30e3da0c1cbed90a96ac853041b" dependencies = [ "encode_unicode", - "lazy_static", "libc", - "unicode-width 0.1.14", - "windows-sys 0.52.0", + "once_cell", + "unicode-width 0.2.0", + "windows-sys 0.59.0", ] [[package]] @@ -569,6 +571,27 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "const_format" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", + "konst", +] + +[[package]] +name = "const_format_proc_macros" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + [[package]] name = "constant_time_eq" version = "0.3.1" @@ -635,9 +658,9 @@ dependencies = [ [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -654,18 +677,18 @@ dependencies = [ [[package]] name = "crossbeam-queue" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df0346b5d5e76ac2fe4e327c5fd1118d6be7c51dfb18f9b7922923f287471e35" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.20" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" @@ -677,7 +700,7 @@ dependencies = [ "crossterm_winapi", "libc", "mio 0.8.11", - "parking_lot 0.12.3", + "parking_lot", "signal-hook", "signal-hook-mio", "winapi", @@ -723,7 +746,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -734,7 +757,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -748,7 +771,7 @@ dependencies = [ "hashbrown 0.14.5", "lock_api", "once_cell", - "parking_lot_core 0.9.10", + "parking_lot_core", ] [[package]] @@ -785,7 +808,7 @@ checksum = "74ef43543e701c01ad77d3a5922755c6a1d71b22d942cb8042be4994b380caff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -796,7 +819,7 @@ checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -807,7 +830,7 @@ checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -828,7 +851,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -838,7 +861,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -859,7 +882,7 @@ dependencies = [ "convert_case", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "unicode-xid", ] @@ -904,7 +927,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -930,9 +953,9 @@ dependencies = [ [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -989,7 +1012,7 @@ checksum = "311a6d2f1f9d60bff73d2c78a0af97ed27f79672f15c238192a5bbb64db56d00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1004,9 +1027,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "flate2" @@ -1015,7 +1038,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" dependencies = [ "crc32fast", - "miniz_oxide 0.8.0", + "miniz_oxide 0.8.2", ] [[package]] @@ -1115,7 +1138,7 @@ checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" dependencies = [ "futures-core", "lock_api", - "parking_lot 0.12.3", + "parking_lot", ] [[package]] @@ -1132,7 +1155,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1224,9 +1247,9 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "governor" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0746aa765db78b521451ef74221663b57ba595bf83f75d0ce23cc09447c8139f" +checksum = "842dc78579ce01e6a1576ad896edc92fca002dd60c9c3746b7fc2bec6fb429d0" dependencies = [ "cfg-if", "dashmap", @@ -1235,7 +1258,7 @@ dependencies = [ "futures-util", "no-std-compat", "nonzero_ext", - "parking_lot 0.12.3", + "parking_lot", "portable-atomic", "quanta", "rand", @@ -1302,12 +1325,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hex" version = "0.4.3" @@ -1334,18 +1351,18 @@ dependencies = [ [[package]] name = "home" -version = "0.5.9" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "http" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", @@ -1389,9 +1406,9 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "1.5.1" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97818827ef4f364230e16705d4706e2897df2bb60617d6ca15d598025a3c481f" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", @@ -1409,9 +1426,9 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.3" +version = "0.27.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +checksum = "f6884a48c6826ec44f524c7456b163cebe9e55a18d7b5e307cb4f100371cc767" dependencies = [ "futures-util", "http", @@ -1597,7 +1614,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1635,9 +1652,9 @@ checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" [[package]] name = "indexmap" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f" dependencies = [ "equivalent", "hashbrown 0.15.2", @@ -1691,15 +1708,6 @@ dependencies = [ "unicode-width 0.1.14", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.10.1" @@ -1738,13 +1746,29 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9" +checksum = "6717b6b5b077764fb5966237269cb3c64edddde4b14ce42647430a78ced9e7b7" dependencies = [ + "once_cell", "wasm-bindgen", ] +[[package]] +name = "konst" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330f0e13e6483b8c34885f7e6c9f19b1a7bd449c673fbb948a51c99d66ef74f4" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "lazy_static" version = "1.5.0" @@ -1756,9 +1780,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.166" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2ccc108bbc0b1331bd061864e7cd823c0cab660bbe6970e66e2c0614decde36" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libm" @@ -1865,9 +1889,9 @@ dependencies = [ [[package]] name = "macon" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4891d8c9ae24ccfa7171265e4492d78fdd71b433f7fa2be4c122b6714b7bbcd" +checksum = "4e2360fdc60235ad3db2d65bb74539e0813c20a626f7e330ce9a64b2d4e513f0" dependencies = [ "macon_api", "macon_derive", @@ -1875,19 +1899,19 @@ dependencies = [ [[package]] name = "macon_api" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f1b2c1498f871034a106d49170c7cfdaf67713b3f5a11dd715f845c5a4192f0" +checksum = "2330ee9375fc537d78cc438ed16695a5d0201f0be9d977bf6bc4ac11e5b4e177" [[package]] name = "macon_derive" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eeee892592f54e656957c5bdd548b37758b14afcffc6c334671e3881c0f00c1" +checksum = "ccd7e1436ba066ad10c8b43ac4a00d304322831b90f039f1d83815f846fee024" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -1929,9 +1953,9 @@ dependencies = [ [[package]] name = "miniz_oxide" -version = "0.8.0" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" +checksum = "4ffbe83022cedc1d264172192511ae958937694cd57ce297164951b8b3568394" dependencies = [ "adler2", ] @@ -1950,11 +1974,10 @@ dependencies = [ [[package]] name = "mio" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ - "hermit-abi", "libc", "wasi", "windows-sys 0.52.0", @@ -1963,11 +1986,12 @@ dependencies = [ [[package]] name = "musicbrainz-db-lite" version = "0.1.0" -source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#78f42aa64e8da0e6918ed127efa8da449c7c80e9" +source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#57e90d3f810ae8a63a80c0bcf95b9f4828d9890e" dependencies = [ "async-fn-stream", "async-trait", "chrono", + "const_format", "extend", "futures", "itertools", @@ -1979,26 +2003,26 @@ dependencies = [ "serde", "serde_json", "sqlx", - "thiserror 2.0.6", + "thiserror 2.0.8", "tracing", ] [[package]] name = "musicbrainz_db_lite_macros" version = "0.1.0" -source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#78f42aa64e8da0e6918ed127efa8da449c7c80e9" +source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#57e90d3f810ae8a63a80c0bcf95b9f4828d9890e" dependencies = [ "darling", "proc-macro2", "quote", "sqlx", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "musicbrainz_db_lite_schema" version = "0.1.1" -source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#78f42aa64e8da0e6918ed127efa8da449c7c80e9" +source = "git+https://github.com/RustyNova016/musicbrainz_db_lite.git?branch=develop#57e90d3f810ae8a63a80c0bcf95b9f4828d9890e" dependencies = [ "sqlx", ] @@ -2006,7 +2030,7 @@ dependencies = [ [[package]] name = "musicbrainz_rs_nova" version = "0.8.0" -source = "git+https://github.com/RustyNova016/musicbrainz_rs_nova.git?branch=develop#f53aee31d47c7d896bb123b39538aa9a73573852" +source = "git+https://github.com/RustyNova016/musicbrainz_rs_nova.git?branch=develop#cbdab4414f4f8680cf580e32a198e63bc4146b25" dependencies = [ "chrono", "glob", @@ -2016,8 +2040,8 @@ dependencies = [ "reqwest", "serde", "serde_json", - "thiserror 2.0.6", - "wasm-timer", + "thiserror 2.0.8", + "tokio", ] [[package]] @@ -2175,7 +2199,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2220,17 +2244,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.3" @@ -2238,21 +2251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", - "parking_lot_core 0.9.10", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2263,7 +2262,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.7", + "redox_syscall", "smallvec", "windows-targets 0.52.6", ] @@ -2399,9 +2398,9 @@ dependencies = [ [[package]] name = "quanta" -version = "0.12.3" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" +checksum = "773ce68d0bb9bc7ef20be3536ffe94e223e1f365bd374108b2659fac0c65cfe6" dependencies = [ "crossbeam-utils", "libc", @@ -2488,18 +2487,9 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.5.7" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" dependencies = [ "bitflags 2.6.0", ] @@ -2694,22 +2684,22 @@ checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" [[package]] name = "rustix" -version = "0.38.41" +version = "0.38.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6" +checksum = "f93dc38ecbab2eb790ff964bb77fa94faf256fd3e73285fd7ba0903b76bedb85" dependencies = [ "bitflags 2.6.0", "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.23.19" +version = "0.23.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +checksum = "5065c3f250cbd332cd894be57c40fa52387247659b14a2d6041d121547903b1b" dependencies = [ "once_cell", "rustls-pki-types", @@ -2729,9 +2719,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.10.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" +checksum = "d2bf47e6ff922db3825eb750c4e2ff784c6ff8fb9e13046ef6a1d1c5401b0b37" [[package]] name = "rustls-webpki" @@ -2744,6 +2734,12 @@ dependencies = [ "untrusted", ] +[[package]] +name = "rustversion" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e819f2bc632f285be6d7cd36e25940d45b2391dd6d9b939e79de557f7014248" + [[package]] name = "ryu" version = "1.0.18" @@ -2752,9 +2748,9 @@ checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" [[package]] name = "scc" -version = "2.2.5" +version = "2.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b202022bb57c049555430e11fc22fea12909276a80a4c3d368da36ac1d88ed" +checksum = "94b13f8ea6177672c49d12ed964cca44836f59621981b04a3e26b87e675181de" dependencies = [ "sdd", ] @@ -2776,9 +2772,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sdd" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49c1eeaf4b6a87c7479688c6d52b9f1153cedd3c489300564f932b065c6eab95" +checksum = "478f121bb72bbf63c52c93011ea1791dca40140dfe13f8336c4c5ac952c33aa9" [[package]] name = "seahash" @@ -2801,9 +2797,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.12.1" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa39c7303dc58b5543c94d22c1766b0d31f2ee58306363ea622b10bbc075eaa2" +checksum = "1863fd3768cd83c56a7f60faa4dc0d403f1b6df0a38c3c25f44b7894e45370d5" dependencies = [ "core-foundation-sys", "libc", @@ -2826,7 +2822,7 @@ checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -2862,7 +2858,7 @@ dependencies = [ "futures", "log", "once_cell", - "parking_lot 0.12.3", + "parking_lot", "scc", "serial_test_derive", ] @@ -2875,7 +2871,7 @@ checksum = "5d69265a08751de7844521fd15003ae0a888e035773ba05695c5c759a6f89eef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3095,7 +3091,7 @@ dependencies = [ "quote", "sqlx-core", "sqlx-macros-core", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3118,7 +3114,7 @@ dependencies = [ "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", - "syn 2.0.89", + "syn 2.0.90", "tempfile", "tokio", "url", @@ -3250,6 +3246,19 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.90", +] + [[package]] name = "subtle" version = "2.6.1" @@ -3269,9 +3278,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.89" +version = "2.0.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d46482f1c1c87acd84dea20c1bf5ebff4c757009ed6bf19cfd36fb10e92c4e" +checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31" dependencies = [ "proc-macro2", "quote", @@ -3295,7 +3304,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3340,9 +3349,9 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef" +checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9" dependencies = [ "rustix", "windows-sys 0.59.0", @@ -3359,11 +3368,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec2a1820ebd077e2b90c4df007bebf344cd394098a13c563957d0afc83ea47" +checksum = "08f5383f3e0071702bf93ab5ee99b52d26936be9dedd9413067cbdcddcb6141a" dependencies = [ - "thiserror-impl 2.0.6", + "thiserror-impl 2.0.8", ] [[package]] @@ -3374,18 +3383,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "thiserror-impl" -version = "2.0.6" +version = "2.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d65750cab40f4ff1929fb1ba509e9914eb756131cef4210da8d5d700d26f6312" +checksum = "f2f357fcec90b3caef6623a099691be676d033b40a058ac95d2a6ade6fa0c943" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3400,9 +3409,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" dependencies = [ "deranged", "num-conv", @@ -3451,8 +3460,8 @@ dependencies = [ "backtrace", "bytes", "libc", - "mio 1.0.2", - "parking_lot 0.12.3", + "mio 1.0.3", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -3468,7 +3477,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3483,20 +3492,19 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" dependencies = [ "rustls", - "rustls-pki-types", "tokio", ] [[package]] name = "tokio-stream" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4e6ce100d0eb49a2734f8c0812bcd324cf357d21810932c5df6b96ef2b86f1" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -3505,9 +3513,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.12" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" dependencies = [ "bytes", "futures-core", @@ -3559,7 +3567,7 @@ checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -3574,9 +3582,9 @@ dependencies = [ [[package]] name = "tracing-error" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" +checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" dependencies = [ "tracing", "tracing-subscriber", @@ -3627,9 +3635,9 @@ checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df" [[package]] name = "unicode-bidi" -version = "0.3.17" +version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" [[package]] name = "unicode-ident" @@ -3764,9 +3772,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e" +checksum = "a474f6281d1d70c17ae7aa6a613c87fce69a127e2624002df63dcb39d6cf6396" dependencies = [ "cfg-if", "once_cell", @@ -3775,36 +3783,36 @@ dependencies = [ [[package]] name = "wasm-bindgen-backend" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358" +checksum = "5f89bb38646b4f81674e8f5c3fb81b562be1fd936d84320f3264486418519c79" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.45" +version = "0.4.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7ec4f8827a71586374db3e87abdb5a2bb3a15afed140221307c3ec06b1f63b" +checksum = "38176d9b44ea84e9184eff0bc34cc167ed044f816accfe5922e54d84cf48eca2" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56" +checksum = "2cc6181fd9a7492eef6fef1f33961e3695e4579b9872a6f7c83aee556666d4fe" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -3812,43 +3820,28 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.95" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" +checksum = "30d7a95b763d3c45903ed6c81f156801839e5ee968bb07e534c44df0fcd330c2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d" - -[[package]] -name = "wasm-timer" -version = "0.2.5" +version = "0.2.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] +checksum = "943aab3fdaaa029a6e0271b35ea10b72b943135afe9bffca82384098ad0e06a6" [[package]] name = "web-sys" -version = "0.3.72" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6488b90108c040df0fe62fa815cbdee25124641df01814dd7282749234c6112" +checksum = "04dd7223427d52553d3702c004d3b2fe07c148165faa56313cb00211e31c12bc" dependencies = [ "js-sys", "wasm-bindgen", @@ -3870,7 +3863,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall 0.5.7", + "redox_syscall", "wasite", ] @@ -4133,7 +4126,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -4155,7 +4148,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4175,7 +4168,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", "synstructure", ] @@ -4196,7 +4189,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] @@ -4218,14 +4211,14 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.89", + "syn 2.0.90", ] [[package]] name = "zip" -version = "2.2.1" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99d52293fc86ea7cf13971b3bb81eb21683636e7ae24c729cdaf1b7c4157a352" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" dependencies = [ "aes", "arbitrary", @@ -4243,7 +4236,7 @@ dependencies = [ "pbkdf2", "rand", "sha1", - "thiserror 2.0.6", + "thiserror 2.0.8", "time", "zeroize", "zopfli", diff --git a/Cargo.toml b/Cargo.toml index 3d2d1412..8fc28c81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "alistral" -version = "0.4.1" +version = "0.4.2" edition = "2021" repository = "https://github.com/RustyNova016/alistral" publish = true @@ -27,7 +27,7 @@ clap = { version = "4.5.23", features = [ "derive", "color", ] } -clap_complete = "4.5.32" +clap_complete = "4.5.40" chrono = "0.4.39" serde = { version = "1.0.216", features = ["rc"] } serde_json = "1.0.128" @@ -43,7 +43,7 @@ tokio = { version = "1.42.0", features = ["full"] } rayon = "1.10.0" reqwest = { version = "0.12.8", features = ["json"] } rand = "0.8.5" -thiserror = "2.0.6" +thiserror = "2.0.8" derive_more = { version = "1.0.0", features = ["full"] } derive-new = "0.7.0" derive-getters = "0.5.0" @@ -56,10 +56,12 @@ serial_test = "3.1.1" sqlx = { version = "0.8.2", features = ["runtime-tokio", "macros"] } tracing = "0.1.41" tracing-subscriber = "0.3.19" -macon = "1.2.0" +macon = "1.3.0" async-fn-stream = "0.2.2" -zip = "2.2.0" +zip = "2.2.2" indoc = "2.0.5" +strsim = "0.11.1" +strum_macros = "0.26.4" # The profile that 'cargo dist' will build with [profile.dist] diff --git a/src/datastructures/clippy/missing_release_barcode.rs b/src/datastructures/clippy/missing_release_barcode.rs index 5138de87..d15180fd 100644 --- a/src/datastructures/clippy/missing_release_barcode.rs +++ b/src/datastructures/clippy/missing_release_barcode.rs @@ -1,6 +1,7 @@ use musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity; use musicbrainz_db_lite::models::musicbrainz::release::Release; +use crate::models::clippy::lint_severity::LintSeverity; use crate::models::clippy::{MbClippyLint, MbClippyLintLink}; use crate::utils::cli::display::ReleaseExt; @@ -72,4 +73,8 @@ impl MbClippyLint for MissingBarcodeLint { Ok(hints) } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::MissingData + } } diff --git a/src/datastructures/clippy/missing_remix_rel.rs b/src/datastructures/clippy/missing_remix_rel.rs new file mode 100644 index 00000000..f9880194 --- /dev/null +++ b/src/datastructures/clippy/missing_remix_rel.rs @@ -0,0 +1,98 @@ +use musicbrainz_db_lite::models::musicbrainz::{main_entities::MainEntity, recording::Recording}; + +use crate::models::clippy::lint_severity::LintSeverity; +use crate::models::clippy::{MbClippyLint, MbClippyLintLink}; +use crate::utils::cli::display::RecordingExt; +use crate::utils::extensions::db_lite_ext::RelationRecordingArtistExt; +use crate::utils::extensions::db_lite_ext::RelationRecordingRecordingExt; + +pub struct MissingRemixRelLint { + recording: Recording, +} + +impl MbClippyLint for MissingRemixRelLint { + fn get_name() -> &'static str { + "missing_remix_rel" + } + + async fn check( + conn: &mut sqlx::SqliteConnection, + entity: &MainEntity, + ) -> Result, crate::Error> { + let MainEntity::Recording(recording) = entity else { + return Ok(None); + }; + + let recording_rels = recording.get_artist_relations(conn).await?; + let mut is_remix = false; + for relation in recording_rels { + if relation.is_remixer_rel(recording) { + is_remix = true; + } + } + + if !is_remix { + return Ok(None); + } + + let artist_relations = recording.get_recording_relations(conn).await?; + // Check if a remixer relationship is missing + for relation in artist_relations { + if relation.is_remix_of_rel(recording) { + return Ok(None); + } + } + + let lint = Self { + recording: recording.clone(), + }; + + Ok(Some(lint)) + } + + async fn get_body( + &self, + conn: &mut sqlx::SqliteConnection, + ) -> Result { + Ok(format!( + "Recording \"{}\" has a remixer relationship, but no `remix of` relationship. +-> Add the original recording as an recording relationship", + self.recording + .pretty_format_with_credits(conn, false) + .await? + )) + } + + async fn get_links( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + let mut out = Vec::new(); + + out.push(MbClippyLintLink { + name: "Recording".to_string(), + url: format!("https://musicbrainz.org/recording/{}", self.recording.mbid), + }); + + out.push(MbClippyLintLink { + name: "Recording editing".to_string(), + url: format!( + "https://musicbrainz.org/recording/{}/edit", + self.recording.mbid + ), + }); + + Ok(out) + } + + async fn get_hints( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + Ok(Vec::new()) + } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::MissingRelation + } +} diff --git a/src/datastructures/clippy/missing_remixer_rel.rs b/src/datastructures/clippy/missing_remixer_rel.rs new file mode 100644 index 00000000..5408a079 --- /dev/null +++ b/src/datastructures/clippy/missing_remixer_rel.rs @@ -0,0 +1,98 @@ +use musicbrainz_db_lite::models::musicbrainz::{main_entities::MainEntity, recording::Recording}; + +use crate::models::clippy::lint_severity::LintSeverity; +use crate::models::clippy::{MbClippyLint, MbClippyLintLink}; +use crate::utils::cli::display::RecordingExt; +use crate::utils::extensions::db_lite_ext::RelationRecordingArtistExt; +use crate::utils::extensions::db_lite_ext::RelationRecordingRecordingExt; + +pub struct MissingRemixerRelLint { + recording: Recording, +} + +impl MbClippyLint for MissingRemixerRelLint { + fn get_name() -> &'static str { + "missing_remixer_rel" + } + + async fn check( + conn: &mut sqlx::SqliteConnection, + entity: &MainEntity, + ) -> Result, crate::Error> { + let MainEntity::Recording(recording) = entity else { + return Ok(None); + }; + + let recording_rels = recording.get_recording_relations(conn).await?; + let mut is_remix = false; + for relation in recording_rels { + if relation.is_remix_of_rel(recording) { + is_remix = true; + } + } + + if !is_remix { + return Ok(None); + } + + let artist_relations = recording.get_artist_relations(conn).await?; + // Check if a remixer relationship is missing + for relation in artist_relations { + if relation.is_remixer_rel(recording) { + return Ok(None); + } + } + + let lint = Self { + recording: recording.clone(), + }; + + Ok(Some(lint)) + } + + async fn get_body( + &self, + conn: &mut sqlx::SqliteConnection, + ) -> Result { + Ok(format!( + "Recording \"{}\" has a remix relationship, but no remixer relationship. +-> Add the remixer as an artist relationship", + self.recording + .pretty_format_with_credits(conn, false) + .await? + )) + } + + async fn get_links( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + let mut out = Vec::new(); + + out.push(MbClippyLintLink { + name: "Recording".to_string(), + url: format!("https://musicbrainz.org/recording/{}", self.recording.mbid), + }); + + out.push(MbClippyLintLink { + name: "Recording editing".to_string(), + url: format!( + "https://musicbrainz.org/recording/{}/edit", + self.recording.mbid + ), + }); + + Ok(out) + } + + async fn get_hints( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + Ok(Vec::new()) + } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::MissingRelation + } +} diff --git a/src/datastructures/clippy/missing_work.rs b/src/datastructures/clippy/missing_work.rs index 7215e8b5..f934ec84 100644 --- a/src/datastructures/clippy/missing_work.rs +++ b/src/datastructures/clippy/missing_work.rs @@ -1,5 +1,6 @@ use musicbrainz_db_lite::models::musicbrainz::{main_entities::MainEntity, recording::Recording}; +use crate::models::clippy::lint_severity::LintSeverity; use crate::models::clippy::MbClippyLintHint; use crate::models::clippy::{MbClippyLint, MbClippyLintLink}; use crate::utils::cli::display::RecordingExt; @@ -80,4 +81,8 @@ impl MbClippyLint for MissingWorkLint { // TODO: Remix hint Ok(hints) } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::MissingData + } } diff --git a/src/datastructures/clippy/mod.rs b/src/datastructures/clippy/mod.rs index 4a8e022a..5b739368 100644 --- a/src/datastructures/clippy/mod.rs +++ b/src/datastructures/clippy/mod.rs @@ -1,3 +1,7 @@ +pub mod missing_remix_rel; +pub mod missing_remixer_rel; +pub mod soundtrack_without_disambiguation; +pub mod suspicious_remix; //pub mod missing_work_language; // Need work languages pub mod missing_release_barcode; pub mod missing_work; diff --git a/src/datastructures/clippy/soundtrack_without_disambiguation.rs b/src/datastructures/clippy/soundtrack_without_disambiguation.rs new file mode 100644 index 00000000..c4852d68 --- /dev/null +++ b/src/datastructures/clippy/soundtrack_without_disambiguation.rs @@ -0,0 +1,73 @@ +use color_eyre::owo_colors::OwoColorize; +use musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity; +use musicbrainz_db_lite::models::musicbrainz::work::Work; + +use crate::models::clippy::lint_severity::LintSeverity; +use crate::models::clippy::MbClippyLint; +use crate::models::clippy::MbClippyLintLink; +use crate::utils::cli::display::WorkExt as _; + +pub struct SoundtrackWithoutDisambiguationLint { + work: Work, +} + +impl MbClippyLint for SoundtrackWithoutDisambiguationLint { + fn get_name() -> &'static str { + "soundtrack_without_disambiguation" + } + + async fn check( + _conn: &mut sqlx::SqliteConnection, + entity: &musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity, + ) -> Result, crate::Error> { + let MainEntity::Work(work) = entity else { + return Ok(None); + }; + + if work.work_type.as_ref().is_none_or(|t| t != "Soundtrack") + || work.disambiguation.as_ref().is_some_and(|d| !d.is_empty()) + { + return Ok(None); + } + + Ok(Some(Self { work: work.clone() })) + } + + async fn get_body( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result { + Ok(format!("Work \"{}\" has type `Soundtrack` and has no disambiguation. + -> Style guidelines for soundtrack works require the name of the original work to be in the disambiguation. + Additionally, if possible, a descriptive name, or the name of the episode" + , self.work.pretty_format().await?)) + } + + async fn get_hints( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + Ok(Vec::new()) + } + + async fn get_links( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + Ok(vec![ + MbClippyLintLink { + name: "Style Guidelines".truecolor(232, 133, 58).to_string(), + url: "https://musicbrainz.org/doc/Style/Specific_types_of_releases/Soundtrack#Work" + .to_string(), + }, + MbClippyLintLink { + name: "Work editing".to_string(), + url: format!("https://musicbrainz.org/work/{}/edit", self.work.mbid), + }, + ]) + } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::StyleIssue + } +} diff --git a/src/datastructures/clippy/suspicious_remix.rs b/src/datastructures/clippy/suspicious_remix.rs new file mode 100644 index 00000000..5ff624f2 --- /dev/null +++ b/src/datastructures/clippy/suspicious_remix.rs @@ -0,0 +1,90 @@ +use musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity; +use musicbrainz_db_lite::models::musicbrainz::recording::Recording; +use regex::Regex; + +use crate::models::clippy::lint_severity::LintSeverity; +use crate::models::clippy::MbClippyLint; +use crate::models::clippy::MbClippyLintLink; +use crate::utils::cli::display::RecordingExt as _; +use crate::utils::extensions::db_lite_ext::RecordingExt; + +pub struct SuspiciousRemixLint { + recording: Recording, +} + +impl MbClippyLint for SuspiciousRemixLint { + fn get_name() -> &'static str { + "suspicious_remix" + } + + async fn check( + conn: &mut sqlx::SqliteConnection, + entity: &musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity, + ) -> Result, crate::Error> { + let MainEntity::Recording(recording) = entity else { + return Ok(None); + }; + + // Check if the title is suspiciously like a remix or vip + let regex = Regex::new(r"(?mi)(((\(|\s))remix(\)|$))|(((\(|\s))vip(\)|$))").unwrap(); + + if !regex.is_match(&recording.title) { + return Ok(None); + } + + // Then check if the links have been set + if recording.is_remix(conn).await? { + return Ok(None); + } + + Ok(Some(Self { + recording: recording.clone(), + })) + } + + async fn get_body( + &self, + conn: &mut sqlx::SqliteConnection, + ) -> Result { + Ok(format!( + "Recording \"{}\" seems to be a remix, but no remix relations have been set +-> Add a `remix of` and `remixer` relationships to the recording", + self.recording + .pretty_format_with_credits(conn, false) + .await? + )) + } + + async fn get_links( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + let mut out = Vec::new(); + + out.push(MbClippyLintLink { + name: "Recording".to_string(), + url: format!("https://musicbrainz.org/recording/{}", self.recording.mbid), + }); + + out.push(MbClippyLintLink { + name: "Recording editing".to_string(), + url: format!( + "https://musicbrainz.org/recording/{}/edit", + self.recording.mbid + ), + }); + + Ok(out) + } + + async fn get_hints( + &self, + _conn: &mut sqlx::SqliteConnection, + ) -> Result, crate::Error> { + Ok(Vec::new()) + } + + fn get_severity(&self) -> crate::models::clippy::lint_severity::LintSeverity { + LintSeverity::MissingRelation + } +} diff --git a/src/datastructures/entity_with_listens/recording_with_listens/mod.rs b/src/datastructures/entity_with_listens/recording_with_listens/mod.rs index cdc79398..5e909d60 100644 --- a/src/datastructures/entity_with_listens/recording_with_listens/mod.rs +++ b/src/datastructures/entity_with_listens/recording_with_listens/mod.rs @@ -135,13 +135,21 @@ impl RecordingWithListens { } pub fn overdue_by(&self) -> Duration { + self.overdue_by_at(&Utc::now()) + } + + pub fn overdue_by_at(&self, date: &DateTime) -> Duration { self.estimated_date_of_next_listen() - .map(|next_listen| Utc::now() - next_listen) + .map(|next_listen| *date - next_listen) .unwrap_or_else(Duration::zero) } pub fn overdue_factor(&self) -> Decimal { - Decimal::from_i64(self.overdue_by().num_seconds()) + self.overdue_factor_at(&Utc::now()) + } + + pub fn overdue_factor_at(&self, date: &DateTime) -> Decimal { + Decimal::from_i64(self.overdue_by_at(date).num_seconds()) .unwrap() .checked_div( Decimal::from_i64(self.average_duration_between_listens().num_seconds()).unwrap(), diff --git a/src/datastructures/radio/sorters/overdue.rs b/src/datastructures/radio/sorters/overdue.rs index dd63d93f..0a2dd72a 100644 --- a/src/datastructures/radio/sorters/overdue.rs +++ b/src/datastructures/radio/sorters/overdue.rs @@ -1,5 +1,9 @@ use core::cmp::Reverse; +use async_fn_stream::fn_stream; +use chrono::Duration; +use chrono::Utc; +use futures::Stream; use rust_decimal::Decimal; use crate::datastructures::entity_with_listens::recording_with_listens::RecordingWithListens; @@ -38,3 +42,44 @@ pub fn overdue_factor_sorter( recordings } + +pub fn overdue_factor_sorter_cumulative( + mut recordings: Vec, +) -> impl Stream { + let conf = Config::load_or_panic(); + + fn_stream(|emitter| async move { + let mut curr_time = Utc::now(); + while !recordings.is_empty() { + let top_recording = recordings + .iter() + .enumerate() + .max_by_key(|r| { + let score = r.1.overdue_factor_at(&curr_time) + Decimal::ONE; + score + * conf + .read_or_panic() + .bumps + .get_multiplier(&r.1.recording().mbid) + }) + .expect("There should be at least one recording"); + + let top_recording = recordings.remove(top_recording.0); + + let score = (top_recording.overdue_factor_at(&curr_time) + Decimal::ONE) + * conf + .read_or_panic() + .bumps + .get_multiplier(&top_recording.recording().mbid); + + println!("{}: {score}", top_recording.recording().title); + + curr_time += top_recording + .recording() + .length_as_duration() + .unwrap_or(Duration::zero()); + + emitter.emit(top_recording).await; + } + }) +} diff --git a/src/models/cli/listens.rs b/src/models/cli/listens.rs index a903f836..b7baff96 100644 --- a/src/models/cli/listens.rs +++ b/src/models/cli/listens.rs @@ -3,6 +3,7 @@ use clap::Subcommand; use crate::models::config::Config; use crate::tools::listens::mapper::listen_mapper_convert_mbids; +use crate::tools::listens::wrong_mapping::wrong_mapping; use crate::utils::cli::read_mbid_from_input; #[derive(Parser, Debug, Clone)] @@ -34,6 +35,11 @@ pub enum ListenSubcommands { /// Your account token token: Option, }, + + WrongMapping { + /// Your username + username: Option, + }, } impl ListenSubcommands { @@ -55,6 +61,9 @@ impl ListenSubcommands { ) .await; } + Self::WrongMapping { username } => { + wrong_mapping(conn, Config::check_username(username)).await; + } } } } diff --git a/src/models/cli/musicbrainz/mod.rs b/src/models/cli/musicbrainz/mod.rs index 038c345f..e3e1a840 100644 --- a/src/models/cli/musicbrainz/mod.rs +++ b/src/models/cli/musicbrainz/mod.rs @@ -3,6 +3,7 @@ use clap::Subcommand; use crate::tools::musicbrainz::clippy::mb_clippy; use crate::utils::cli::read_mbid_from_input; +use crate::utils::whitelist_blacklist::WhitelistBlacklist; #[derive(Parser, Debug, Clone)] #[command(version, about, long_about = None)] @@ -29,6 +30,14 @@ pub enum MusicbrainzSubcommands { /// Whether to check FILO (first in, last out) instead of FIFO (first in, first out) #[arg(short, long)] new_first: bool, + + /// List of lints that should only be checked (Note: Put this argument last or before another argument) + #[arg(short, long, num_args = 0..)] + whitelist: Option>, + + /// List of lints that should not be checked (Note: Put this argument last or before another argument) + #[arg(short, long, num_args = 0..)] + blacklist: Option>, }, } @@ -38,14 +47,25 @@ impl MusicbrainzSubcommands { Self::Clippy { start_mbid, new_first, + whitelist, + blacklist, } => { let mbid = start_mbid .clone() .unwrap_or_else(|| "8f3471b5-7e6a-48da-86a9-c1c07a0f47ae".to_string()); + let filter = if let Some(whitelist) = whitelist { + WhitelistBlacklist::WhiteList(whitelist.clone()) + } else if let Some(blacklist) = blacklist { + WhitelistBlacklist::BlackList(blacklist.clone()) + } else { + WhitelistBlacklist::BlackList(Vec::new()) + }; + mb_clippy( &read_mbid_from_input(&mbid).expect("Couldn't read mbid"), *new_first, + &filter, ) .await; } diff --git a/src/models/cli/radio.rs b/src/models/cli/radio.rs index fa0db34a..79b1ce64 100644 --- a/src/models/cli/radio.rs +++ b/src/models/cli/radio.rs @@ -174,6 +174,12 @@ pub enum RadioSubcommands { /// Instead of sorting by date, the listens are sorted by how many estimated listens should have happened by now (Time elapsed since last listen / Average time per listens) #[arg(short, long, default_value_t = false)] overdue_factor: bool, + + /// Makes `overdue_factor` more accurate by calculating the score at the time the listen will be listened at instead of now. + /// + /// This may slowdown the playlist creation by a lot! + #[arg(short, long, default_value_t = false)] + at_listening_time: bool, }, } @@ -232,6 +238,7 @@ impl RadioSubcommands { min, cooldown, overdue_factor: delay_factor, + at_listening_time, } => { overdue_radio( conn, @@ -241,6 +248,7 @@ impl RadioSubcommands { *cooldown, *delay_factor, command.get_collector(), + *at_listening_time, ) .await?; } diff --git a/src/models/clippy/lint_severity.rs b/src/models/clippy/lint_severity.rs new file mode 100644 index 00000000..4edfda77 --- /dev/null +++ b/src/models/clippy/lint_severity.rs @@ -0,0 +1,17 @@ +pub enum LintSeverity { + MissingData, + MissingRelation, + WrongData, + StyleIssue, +} + +impl LintSeverity { + pub fn get_color(&self) -> (u8, u8, u8) { + match self { + Self::MissingData => (32, 117, 191), + Self::MissingRelation => (141, 102, 226), + Self::WrongData => (191, 45, 32), + Self::StyleIssue => (232, 133, 58), + } + } +} diff --git a/src/models/clippy.rs b/src/models/clippy/mod.rs similarity index 92% rename from src/models/clippy.rs rename to src/models/clippy/mod.rs index 0ca688ca..64bcb0ee 100644 --- a/src/models/clippy.rs +++ b/src/models/clippy/mod.rs @@ -1,8 +1,11 @@ use std::fmt::Display; use color_eyre::owo_colors::OwoColorize; +use lint_severity::LintSeverity; use musicbrainz_db_lite::models::musicbrainz::main_entities::MainEntity; +pub mod lint_severity; + pub trait MbClippyLint: Sized { async fn check( conn: &mut sqlx::SqliteConnection, @@ -25,6 +28,8 @@ pub trait MbClippyLint: Sized { &self, conn: &mut sqlx::SqliteConnection, ) -> Result, crate::Error>; + + fn get_severity(&self) -> LintSeverity; } pub struct MbClippyLintLink { diff --git a/src/models/config/mod.rs b/src/models/config/mod.rs index 9f38e42c..64638992 100644 --- a/src/models/config/mod.rs +++ b/src/models/config/mod.rs @@ -1,3 +1,4 @@ +pub mod whitelisted_wrong_mappings; use bumps::BumpList; use clap::CommandFactory; use config_guard::ConfigGuard; diff --git a/src/models/config/whitelisted_wrong_mappings.rs b/src/models/config/whitelisted_wrong_mappings.rs new file mode 100644 index 00000000..1456c3e6 --- /dev/null +++ b/src/models/config/whitelisted_wrong_mappings.rs @@ -0,0 +1,38 @@ +use serde::Deserialize; +use serde::Serialize; + +use super::config_trait::ConfigFile; + +#[derive(Debug, Default, Deserialize, Serialize)] +pub struct WhilistedWrongMappings(Vec); + +#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Eq)] +struct WrongMapping { + msid: String, + mbid: String, +} + +impl WhilistedWrongMappings { + pub fn add(&mut self, msid: String, mbid: String) { + let new = WrongMapping { msid, mbid }; + + if !self.0.contains(&new) { + self.0.push(new); + } + } + + pub fn is_whitelisted(&self, msid: &String, mbid: &String) -> bool { + let new = WrongMapping { + msid: msid.to_string(), + mbid: mbid.to_string(), + }; + + self.0.contains(&new) + } +} + +impl ConfigFile for WhilistedWrongMappings { + fn file_name() -> &'static str { + "wrong_mapping_whitelist.json" + } +} diff --git a/src/tools/listens/mod.rs b/src/tools/listens/mod.rs index d96a0388..572fee07 100644 --- a/src/tools/listens/mod.rs +++ b/src/tools/listens/mod.rs @@ -1,3 +1,4 @@ pub mod import; pub mod mapper; pub mod unlinked; +pub mod wrong_mapping; diff --git a/src/tools/listens/wrong_mapping/display.rs b/src/tools/listens/wrong_mapping/display.rs new file mode 100644 index 00000000..f0508789 --- /dev/null +++ b/src/tools/listens/wrong_mapping/display.rs @@ -0,0 +1,121 @@ +use color_eyre::owo_colors::OwoColorize as _; +use inquire::InquireError; +use inquire::Select; +use listenbrainz::raw::Client; +use musicbrainz_db_lite::models::listenbrainz::listen::Listen; +use musicbrainz_db_lite::models::listenbrainz::messybrainz_submission::MessybrainzSubmission; +use musicbrainz_db_lite::models::musicbrainz::recording::Recording; +use strsim::sorensen_dice; + +use crate::models::config::whitelisted_wrong_mappings::WhilistedWrongMappings; +use crate::utils::cli::display::RecordingExt as _; +use crate::utils::cli::hyperlink_rename; +use crate::utils::extensions::owo_colors_ext::AlistralColors as _; + +pub(super) async fn display_wrong_mapping( + conn: &mut sqlx::SqliteConnection, + config: &mut WhilistedWrongMappings, + messybrainz_data: &MessybrainzSubmission, + recording: &Recording, + listen: &Listen, +) -> bool { + println!(); + println!("{}", "Wrong mapping".to_string().as_title()); + println!(); + println!( + "Listen data {} by {} is currently mapped to {}", + messybrainz_data.recording.truecolor(0, 184, 84), + messybrainz_data.artist_credit.truecolor(0, 143, 229), + recording + .pretty_format_with_credits(conn, true) + .await + .expect("Couldn't format credits") + ); + println!(); + + let title_score = sorensen_dice( + &messybrainz_data.recording.to_lowercase(), + &recording.title.to_lowercase(), + ); + if title_score == 1.0 { + } else if title_score < 0.5 { + println!("Title similarity: {}", title_score.red()); + } else { + println!("Title similarity: {}", title_score.yellow()); + } + + let artist_score = sorensen_dice( + &messybrainz_data.artist_credit.to_lowercase(), + &recording + .get_artist_credits_or_fetch(conn) + .await + .expect("Couldn't get the artist credit") + .to_string() + .to_lowercase(), + ); + if artist_score == 1.0 { + } else if artist_score < 0.5 { + println!("Artist similarity: {}", artist_score.red()); + } else { + println!("Artist similarity: {}", artist_score.yellow()); + } + + println!(); + println!( + "{}", + hyperlink_rename( + &"See listen on listenbrainz".to_string(), + &format!( + "https://listenbrainz.org/user/RustyNova/?max_ts={}", + listen.listened_at + 1 + ) + ) + ); + println!(); + + match choice() { + Choice::Next => { + let lb_client = Client::new(); + Listen::fetch_listen_by_id( + conn, + &lb_client, + listen.listened_at, + &listen.user, + &listen.recording_msid, + 10, + ) + .await + .expect("Couldn't refresh listen"); + true + } + Choice::Whitelist => { + config.add(messybrainz_data.msid.clone(), recording.mbid.clone()); + true + } + Choice::Exit => false, + } +} + +#[derive(strum_macros::Display)] +enum Choice { + Next, + #[strum(to_string = "Whitelist mapping")] + Whitelist, + Exit, +} + +fn choice() -> Choice { + loop { + let options = vec![Choice::Next, Choice::Whitelist, Choice::Exit]; + + let ans = Select::new("", options).prompt(); + + match ans { + Ok(choice) => return choice, + Err(InquireError::OperationCanceled) | Err(InquireError::OperationInterrupted) => { + return Choice::Exit + } + _ => println!("There was an error, please try again"), + } + } +} diff --git a/src/tools/listens/wrong_mapping/mod.rs b/src/tools/listens/wrong_mapping/mod.rs new file mode 100644 index 00000000..dd888011 --- /dev/null +++ b/src/tools/listens/wrong_mapping/mod.rs @@ -0,0 +1,74 @@ +use display::display_wrong_mapping; +use itertools::Itertools; +use musicbrainz_db_lite::models::listenbrainz::messybrainz_submission::MessybrainzSubmission; +use strsim::sorensen_dice; + +use crate::database::listenbrainz::listens::ListenFetchQuery; +use crate::database::listenbrainz::listens::ListenFetchQueryReturn; +use crate::models::config::config_trait::ConfigFile as _; +use crate::models::config::whitelisted_wrong_mappings::WhilistedWrongMappings; + +pub mod display; + +pub async fn wrong_mapping(conn: &mut sqlx::SqliteConnection, username: String) { + let config = WhilistedWrongMappings::load().expect("Couldn't load whitelisted mappings"); + let listens = ListenFetchQuery::builder() + .fetch_recordings_redirects(false) + .returns(ListenFetchQueryReturn::Mapped) + .user(username.to_string()) + .build() + .fetch(conn) + .await + .expect("Couldn't fetch listens"); + + for listen in listens.iter().unique_by(|l| &l.recording_msid) { + let messybrainz_data = + MessybrainzSubmission::find_by_msid(conn, listen.recording_msid.clone()) + .await + .expect("Couldn't fetch the messybrainz data of the listen") + .expect("Couldn't find the messybrainz data of the listen"); + + let recording = listen + .get_recording_or_fetch(conn) + .await + .expect("Couldn't fetch recording data") + .expect("The listen should be mapped"); + + if config + .read_or_panic() + .is_whitelisted(&messybrainz_data.msid, &recording.mbid) + { + continue; + } + + let formated_messy = format!( + "{} {}", + messybrainz_data.artist_credit, messybrainz_data.recording + ) + .to_lowercase(); + let formated_recording = format!( + "{} {}", + recording + .get_artist_credits_or_fetch(conn) + .await + .expect("Couldn't get the artist credit"), + recording.title + ) + .to_lowercase(); + let score = sorensen_dice(&formated_messy, &formated_recording); + + if score != 1.0 { + let continu = display_wrong_mapping( + conn, + &mut config.write_or_panic(), + &messybrainz_data, + &recording, + listen, + ) + .await; + if !continu { + break; + } + } + } +} diff --git a/src/tools/musicbrainz/clippy.rs b/src/tools/musicbrainz/clippy.rs index 2f4b9491..94758b0e 100644 --- a/src/tools/musicbrainz/clippy.rs +++ b/src/tools/musicbrainz/clippy.rs @@ -7,13 +7,19 @@ use musicbrainz_db_lite::models::musicbrainz::recording::Recording; use crate::database::get_conn; use crate::datastructures::clippy::missing_release_barcode::MissingBarcodeLint; +use crate::datastructures::clippy::missing_remix_rel::MissingRemixRelLint; +use crate::datastructures::clippy::missing_remixer_rel::MissingRemixerRelLint; use crate::datastructures::clippy::missing_work::MissingWorkLint; +use crate::datastructures::clippy::soundtrack_without_disambiguation::SoundtrackWithoutDisambiguationLint; +use crate::datastructures::clippy::suspicious_remix::SuspiciousRemixLint; use crate::models::clippy::MbClippyLint; use crate::utils::cli::await_next; use crate::utils::cli::display::MainEntityExt; +use crate::utils::extensions::owo_colors_ext::AlistralColors; use crate::utils::println_cli; +use crate::utils::whitelist_blacklist::WhitelistBlacklist; -pub async fn mb_clippy(start_mbid: &str, new_first: bool) { +pub async fn mb_clippy(start_mbid: &str, new_first: bool, filter: &WhitelistBlacklist) { let mut conn = get_conn().await; let start_node = Recording::fetch_and_save(&mut conn, start_mbid) @@ -38,8 +44,12 @@ pub async fn mb_clippy(start_mbid: &str, new_first: bool) { .await .expect("Couldn't fetch entity"); - check_lint::(&mut conn, &mut entity).await; - check_lint::(&mut conn, &mut entity).await; + check_lint::(&mut conn, &mut entity, filter).await; + check_lint::(&mut conn, &mut entity, filter).await; + check_lint::(&mut conn, &mut entity, filter).await; + check_lint::(&mut conn, &mut entity, filter).await; + check_lint::(&mut conn, &mut entity, filter).await; + check_lint::(&mut conn, &mut entity, filter).await; println!( "Checked {}", @@ -68,7 +78,16 @@ fn get_new_element(queue: &mut VecDeque, new_first: bool) -> Option< } } -async fn check_lint(conn: &mut sqlx::SqliteConnection, entity: &mut MainEntity) { +async fn check_lint( + conn: &mut sqlx::SqliteConnection, + entity: &mut MainEntity, + filter: &WhitelistBlacklist, +) { + // Check if the lint is allowed + if !filter.is_allowed(&L::get_name().to_string()) { + return; + } + let Some(lint) = L::check(conn, entity) .await .expect("Error while processing lint") @@ -76,7 +95,13 @@ async fn check_lint(conn: &mut sqlx::SqliteConnection, entity: return; }; - println!("{}", format!("\n {} ", L::get_name()).on_yellow().black()); + println!( + "{}", + format!("\n {} ", L::get_name()) + .on_truecolor_tup(lint.get_severity().get_color()) + .black() + .bold() + ); println!(); println!( "{}", @@ -164,9 +189,15 @@ async fn get_new_nodes( // #[cfg(test)] // mod tests { // use crate::tools::musicbrainz::clippy::mb_clippy; +// use crate::utils::whitelist_blacklist::WhitelistBlacklist; // #[tokio::test] // async fn mb_clippy_test() { -// mb_clippy("543bb836-fb00-470a-8a27-25941fe0294c", false).await; +// mb_clippy( +// "b67fae1f-3037-4c01-bff9-b5e877220267", +// false, +// &WhitelistBlacklist::default(), +// ) +// .await; // } // } diff --git a/src/tools/radio/overdue.rs b/src/tools/radio/overdue.rs index 10dd5367..d216973d 100644 --- a/src/tools/radio/overdue.rs +++ b/src/tools/radio/overdue.rs @@ -1,3 +1,5 @@ +use core::pin::Pin; + use chrono::Duration; use futures::{stream, StreamExt}; @@ -7,10 +9,14 @@ use crate::datastructures::radio::filters::cooldown::cooldown_filter; use crate::datastructures::radio::filters::min_listens::min_listen_filter; use crate::datastructures::radio::filters::timeouts::timeout_filter; use crate::datastructures::radio::seeders::listens::ListenSeeder; -use crate::datastructures::radio::sorters::overdue::{overdue_factor_sorter, overdue_sorter}; +use crate::datastructures::radio::sorters::overdue::overdue_factor_sorter; +use crate::datastructures::radio::sorters::overdue::overdue_factor_sorter_cumulative; +use crate::datastructures::radio::sorters::overdue::overdue_sorter; use crate::models::playlist_stub::PlaylistStub; use crate::utils::println_cli; +//TODO: Refactor Radios params into structs +#[expect(clippy::too_many_arguments)] pub async fn overdue_radio( conn: &mut sqlx::SqliteConnection, seeder: ListenSeeder, @@ -19,6 +25,7 @@ pub async fn overdue_radio( cooldown: u64, overdue_factor: bool, collector: RadioCollector, + at_listening_time: bool, ) -> color_eyre::Result<()> { let username = seeder.username().clone(); @@ -34,17 +41,23 @@ pub async fn overdue_radio( println_cli("[Filter] Filtering listen timeouts"); let recordings = timeout_filter(recordings); - let recordings: Vec = if !overdue_factor { + let recordings = if !overdue_factor { println_cli("[Sorting] Sorting listen by overdue duration"); - overdue_sorter(recordings.collect().await) - } else { + Box::pin(stream::iter(overdue_sorter(recordings.collect().await))) + as Pin>> + } else if !at_listening_time { println_cli("[Sorting] Sorting listen by overdue factor"); - overdue_factor_sorter(recordings.collect().await) + Box::pin(stream::iter(overdue_factor_sorter( + recordings.collect().await, + ))) as Pin>> + } else { + println_cli("[Sorting] Sorting listen by overdue factor at listen time"); + Box::pin(overdue_factor_sorter_cumulative(recordings.collect().await)) }; println_cli("[Finalising] Creating radio playlist"); let collected = collector - .collect(stream::iter(recordings).map(|r| r.recording().clone())) + .collect(recordings.map(|r| r.recording().clone())) .await; println_cli("[Sending] Sending radio playlist to listenbrainz"); @@ -75,6 +88,7 @@ pub async fn overdue_radio( // .count_default() // .duration_default() // .build(), +// true // ) // .await // .unwrap(); diff --git a/src/utils/cli/constants.rs b/src/utils/cli/constants.rs new file mode 100644 index 00000000..b948b6c3 --- /dev/null +++ b/src/utils/cli/constants.rs @@ -0,0 +1,7 @@ +pub const CLEAR_UNTIL_END_OF_SCREEN: &str = "\u{001b}[0J"; +pub const CLEAR_TO_BEGINNING_OF_SCREEN: &str = "\u{001b}[1J"; +pub const CLEAR_ENTIRE_SCREEN: &str = "\u{001b}[2J"; + +pub const CLEAR_UNTIL_END_OF_LINE: &str = "\u{001b}[0K"; +pub const CLEAR_UNTIL_START_OF_LINE: &str = "\u{001b}[1K"; +pub const CLEAR_ENTIRE_LINE: &str = "\u{001b}[2K"; diff --git a/src/utils/cli/mod.rs b/src/utils/cli/mod.rs index e1425499..a73de865 100644 --- a/src/utils/cli/mod.rs +++ b/src/utils/cli/mod.rs @@ -1,3 +1,6 @@ +pub mod constants; +pub mod navigation; +pub mod prompt; use core::fmt; use core::fmt::Display; use std::io; diff --git a/src/utils/cli/navigation/mod.rs b/src/utils/cli/navigation/mod.rs new file mode 100644 index 00000000..10e6a546 --- /dev/null +++ b/src/utils/cli/navigation/mod.rs @@ -0,0 +1,8 @@ +pub trait NavigationChoice { + fn get_result(&self) -> NavigationResult; +} + +pub enum NavigationResult { + AskAgain, + Ok(T), +} diff --git a/src/utils/cli/prompt.rs b/src/utils/cli/prompt.rs new file mode 100644 index 00000000..6fe60832 --- /dev/null +++ b/src/utils/cli/prompt.rs @@ -0,0 +1,8 @@ +pub trait PromptChoice { + fn get_result(&self) -> NavigationResult; +} + +pub enum NavigationResult { + AskAgain, + Ok(T), +} diff --git a/src/utils/entities/mod.rs b/src/utils/entities/mod.rs new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/src/utils/entities/mod.rs @@ -0,0 +1 @@ + diff --git a/src/utils/extensions/db_lite_ext.rs b/src/utils/extensions/db_lite_ext.rs new file mode 100644 index 00000000..73eff58e --- /dev/null +++ b/src/utils/extensions/db_lite_ext.rs @@ -0,0 +1,43 @@ +use extend::ext; +use musicbrainz_db_lite::models::musicbrainz::artist::Artist; +use musicbrainz_db_lite::models::musicbrainz::recording::Recording; +use musicbrainz_db_lite::models::musicbrainz::relations::Relation; + +#[ext] +pub impl Relation { + fn is_remix_of_rel(&self, base_recording: &Recording) -> bool { + self.relation_type == "remix" + && self.entity0 == base_recording.id + && self.direction == "forward" + } +} + +#[ext] +pub impl Relation { + fn is_remixer_rel(&self, base_recording: &Recording) -> bool { + self.relation_type == "remixer" + && self.entity1 == base_recording.id + && self.direction == "backward" + } +} + +#[ext] +pub impl Recording { + async fn is_remix(&self, conn: &mut sqlx::SqliteConnection) -> Result { + let recording_rels = self.get_artist_relations(conn).await?; + for relation in recording_rels { + if relation.is_remixer_rel(self) { + return Ok(true); + } + } + + let artist_relations = self.get_recording_relations(conn).await?; + for relation in artist_relations { + if relation.is_remix_of_rel(self) { + return Ok(true); + } + } + + Ok(false) + } +} diff --git a/src/utils/extensions/mod.rs b/src/utils/extensions/mod.rs index bc030d46..ccfe266e 100644 --- a/src/utils/extensions/mod.rs +++ b/src/utils/extensions/mod.rs @@ -1,3 +1,5 @@ +pub mod db_lite_ext; +pub mod owo_colors_ext; use chrono::DateTime; use chrono::TimeZone; use chrono::Utc; diff --git a/src/utils/extensions/owo_colors_ext.rs b/src/utils/extensions/owo_colors_ext.rs new file mode 100644 index 00000000..d13e7611 --- /dev/null +++ b/src/utils/extensions/owo_colors_ext.rs @@ -0,0 +1,37 @@ +use core::fmt::Display; + +use color_eyre::owo_colors::OwoColorize; + +use crate::utils::cli::constants::CLEAR_UNTIL_END_OF_LINE; + +pub trait AlistralColors: Display { + fn true_color_tup(&self, color: (u8, u8, u8)) -> String { + self.truecolor(color.0, color.1, color.2).to_string() + } + + fn on_truecolor_tup(&self, color: (u8, u8, u8)) -> String { + self.on_truecolor(color.0, color.1, color.2).to_string() + } + + fn alistral_green(&self) -> String { + self.truecolor(18, 198, 121).to_string() + } + + fn on_alistral_green(&self) -> String { + self.on_truecolor(18, 198, 121).to_string() + } + + fn on_alistral_dark_green(&self) -> String { + self.on_truecolor(0, 165, 93).to_string() + } + + fn as_title(&self) -> String { + format!(" {self} {CLEAR_UNTIL_END_OF_LINE}") + .bold() + .on_alistral_dark_green() + .black() + .to_string() + } +} + +impl AlistralColors for T {} diff --git a/src/utils/logger.rs b/src/utils/logger.rs index 3003d88a..5ca113b7 100644 --- a/src/utils/logger.rs +++ b/src/utils/logger.rs @@ -3,6 +3,8 @@ use color_eyre::owo_colors::OwoColorize; use indicatif::{MultiProgress, ProgressBar}; use std::fmt::Display; +use super::extensions::owo_colors_ext::AlistralColors as _; + pub struct Logger { print_override: Option, //TODO: Keep bar all the time? bar_count: u32, @@ -58,7 +60,7 @@ impl Logger { } pub fn println_cli(&self, string: T) { - self.print(format!("{} {}", "[Alistral]".green(), string)); + self.print(format!("{} {}", "[Alistral]".alistral_green(), string)); } pub fn println_lis(&self, string: T) { diff --git a/src/utils/mod.rs b/src/utils/mod.rs index db19d1e1..a98621c2 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod streams; use std::fmt::Display; use std::sync::{Arc, Mutex}; @@ -11,6 +12,7 @@ use logger::Logger; pub mod cli; pub mod cli_paging; +pub mod entities; pub mod env; pub mod extensions; pub mod listenbrainz_api; @@ -19,6 +21,7 @@ pub mod radio; pub mod regex; pub mod tokio; pub mod traits; +pub mod whitelist_blacklist; #[derive(Clone, Debug, PartialEq, Eq, Builder)] /// Reader for the User Listens endpoint diff --git a/src/utils/streams.rs b/src/utils/streams.rs new file mode 100644 index 00000000..2c8d5c15 --- /dev/null +++ b/src/utils/streams.rs @@ -0,0 +1,15 @@ +use futures::stream; +use futures::Stream; +use futures::StreamExt; + +/// Turns an hard type variable into an opaque type +pub fn into_opaque_stream(val: T) -> impl Stream +where + T: Stream + StreamExt, +{ + val.map(|v| v) +} + +pub fn stream_iter_opaque, U>(val: T) -> impl Stream { + stream::iter(val) +} diff --git a/src/utils/whitelist_blacklist.rs b/src/utils/whitelist_blacklist.rs new file mode 100644 index 00000000..0337d02e --- /dev/null +++ b/src/utils/whitelist_blacklist.rs @@ -0,0 +1,22 @@ +pub enum WhitelistBlacklist { + WhiteList(Vec), + BlackList(Vec), +} + +impl WhitelistBlacklist { + pub fn is_allowed(&self, element: &T) -> bool + where + T: Eq, + { + match &self { + Self::WhiteList(vals) => vals.contains(element), + Self::BlackList(vals) => !vals.contains(element), + } + } +} + +impl Default for WhitelistBlacklist { + fn default() -> Self { + Self::BlackList(Vec::new()) + } +}