diff --git a/.github/workflows/rust-macos-latest b/.github/workflows/rust-macos-latest deleted file mode 100644 index 16ea974..0000000 --- a/.github/workflows/rust-macos-latest +++ /dev/null @@ -1,22 +0,0 @@ -name: Rust - -on: - push: - branches: [ "master" ] - pull_request: - branches: [ "master" ] - -env: - CARGO_TERM_COLOR: always - -jobs: - build: - - runs-on: macos-latest - - steps: - - uses: actions/checkout@v4 - - name: Build - run: cargo build --verbose - - name: Run tests - run: cargo test --verbose diff --git a/.github/workflows/rust-ubuntu-latest.yml b/.github/workflows/rust-ubuntu-latest.yml index 3edcd2a..3a200b9 100644 --- a/.github/workflows/rust-ubuntu-latest.yml +++ b/.github/workflows/rust-ubuntu-latest.yml @@ -4,7 +4,7 @@ on: push: branches: [ "master" ] pull_request: - branches: [ "master" ] + branches: [ "master", "icarus_v2_support" ] env: CARGO_TERM_COLOR: always @@ -17,7 +17,7 @@ jobs: steps: - uses: actions/checkout@v4 - name: Install Rust version - run: rustup install 1.86.0 && rustup default 1.86.0 + run: rustup install 1.88.0 && rustup default 1.88.0 - name: Debug secret run: echo "${{ secrets.MYREPO_TOKEN }}" | head -c 10 ; echo "..." @@ -36,4 +36,4 @@ jobs: EXTREPO_KEY: ${{ secrets.MYREPO_TOKEN }} - name: Run tests - run: cargo test --verbose \ No newline at end of file + run: cargo test --verbose diff --git a/.gitignore b/.gitignore index 450eff1..aa52e8d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,3 @@ /target .vscode/ -Cargo.lock diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index e69de29..0000000 diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f26b708 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1962 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "addr2line" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +dependencies = [ + "gimli", +] + +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aho-corasick" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "backtrace" +version = "0.3.74" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-targets", +] + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "deranged" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "flate2" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "getrandom" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73fea8450eea4bac3940448fb7ae50d91f034f941199fcd9d909a5a07aa455f0" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", +] + +[[package]] +name = "gimli" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" + +[[package]] +name = "h2" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5017294ff4bb30944501348f6f8e42e6ad28f42c8bbef7a74029aff064a4e3c2" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[package]] +name = "http" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc2fdfdbff08affe55bb779f33b053aa1fe5dd5b54c257343c17edfa55711bdb" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icarus-dm" +version = "0.7.0" +dependencies = [ + "futures", + "http", + "icarus_models", + "reqwest", + "serde", + "serde_json", + "tokio", + "tokio-util", + "uuid", +] + +[[package]] +name = "icarus_models" +version = "0.5.6" +source = "git+ssh://git@git.kundeng.us/phoenix/icarus_models.git?tag=v0.5.6#2d6b550ae6721b41ecc3039799f6a5e873869077" +dependencies = [ + "josekit", + "rand", + "serde", + "serde_json", + "time", + "utoipa", + "uuid", +] + +[[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", +] + +[[package]] +name = "idna" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "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]] +name = "indexmap" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3954d50fe15b02142bf25d3b8bdadb634ec3948f103d04ffe3031bc8fe9d7058" +dependencies = [ + "equivalent", + "hashbrown", + "serde", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "josekit" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a808e078330e6af222eb0044b71d4b1ff981bfef43e7bc8133a88234e0c86a0c" +dependencies = [ + "anyhow", + "base64", + "flate2", + "openssl", + "regex", + "serde", + "serde_json", + "thiserror", + "time", +] + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + +[[package]] +name = "linux-raw-sys" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe7db12097d22ec582439daf8618b8fdd1a7bef6270e9af3b1ebcd30893cf413" + +[[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.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e3e04debbb59698c15bacbb6d93584a8c0ca9cc3213cb423d31f760d8843ce5" +dependencies = [ + "adler2", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "object" +version = "0.36.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +dependencies = [ + "memchr", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl" +version = "0.10.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e14130c6a98cd258fdcb0fb6d744152343ff729cbfcb28c656a9d12b999fbcd" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bb61ea9811cc39e3c2069f40b8b8e2e70d8569b361f879786cc7ed48b777cdd" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "parking_lot" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31971752e70b8b2686d7e46ec17fb38dad4051d94024c88df49b667caea9c84" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +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" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23d7fd106d8c02486a8d64e778353d1cffe08ce79ac2e82f540c86d0facf6912" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b9458fa0bfeeac22b5ca447c63aaf45f28439a709ccd244698632f9aa6394d6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" + +[[package]] +name = "reqwest" +version = "0.12.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "mime_guess", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.15", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustix" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e56a18552996ac8d29ecc3b190b4fdbb2d91ca4ec396de7bbffaf43f3d637e96" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.103.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eded382c5f5f786b989652c49544c4877d9f015cc22e145a5ea8ea66c2921cd2" + +[[package]] +name = "ryu" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" + +[[package]] +name = "schannel" +version = "0.1.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.219" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +dependencies = [ + "indexmap", + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook-registry" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "smallvec" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd" + +[[package]] +name = "socket2" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f5fd57c80058a56cf5c777ab8a126398ece8e442983605d280a44ce79d0edef" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b09a44accad81e1ba1cd74a32461ba89dee89095ba17b32f5d03683b1b1fc2a0" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7437ac7763b9b123ccf33c338a5cc1bac6f69b45a136c19bdd8a65e3916435bf" +dependencies = [ + "fastrand", + "getrandom 0.3.2", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3467d614147380f2e4e374161426ff399c91084acd2363eaf549172b3d5e60c0" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c5e1be1c48b9172ee610da68fd9cd2770e7a4056cb3fc98710ee6906f0c7960" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" + +[[package]] +name = "time-macros" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adc82fd73de2a9722ac5da747f12383d2bfdb93591ee6c58486e0097890f05f2" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicase" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" + +[[package]] +name = "unicode-ident" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[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 = "utoipa" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "uuid", +] + +[[package]] +name = "uuid" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +dependencies = [ + "getrandom 0.3.2", + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasi" +version = "0.14.2+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-registry" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b8a9ed28765efc97bbc954883f4e6796c33a06546ebafacbabee9696967499e" +dependencies = [ + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen-rt" +version = "0.39.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" +dependencies = [ + "bitflags", +] + +[[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 = "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", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2586fea28e186957ef732a5f8b3be2da217d65c5969d4b1e17f973ebbe876879" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a996a8f63c5c4448cd959ac1bab0aaa3306ccfd060472f85943ee0750f0169be" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" + +[[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", +] diff --git a/Cargo.toml b/Cargo.toml index 817830f..aa53508 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,21 +1,17 @@ [package] name = "icarus-dm" -version = "0.6.0" -rust-version = "1.86" +version = "0.7.0" +rust-version = "1.88" edition = "2024" - - - # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] futures = { version = "0.3.31" } http = { version = "1.3.1" } -reqwest = { version = "0.12.15", features = ["json", "blocking", "multipart", "stream"] } +reqwest = { version = "0.12.20", features = ["json", "blocking", "multipart", "stream"] } serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" -tokio = { version = "1.44.1", features = ["full"] } -tokio-util = { version = "0.7.14", features = ["codec"] } -uuid = { version = "1.16.0", features = ["v4", "serde"] } -icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.4.3" } +tokio = { version = "1.45.1", features = ["full"] } +tokio-util = { version = "0.7.15", features = ["codec"] } +uuid = { version = "1.17.0", features = ["v4", "serde"] } +icarus_models = { git = "ssh://git@git.kundeng.us/phoenix/icarus_models.git", tag = "v0.5.6" } diff --git a/LICENSE b/LICENSE index a07f809..f5a66d1 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2019 Kun Deng +Copyright (c) 2025 Kun Deng Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index dce72b0..b07bf32 100644 --- a/README.md +++ b/README.md @@ -42,31 +42,31 @@ The program has been built and can be executed by the binary file *icarus-dm*. F ### Downloading Song ```BASH -icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -b e8407fc6-edd2-44c1-993f-08dd7324d91a +icarus-dm download -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -b e8407fc6-edd2-44c1-993f-08dd7324d91a ``` ### Uploading Song with metadata ```BASH -icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -s /path/of/song.flac -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png +icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -s /path/of/song.flac -t 1 -m /path/to/metadata/config/collection.json -ca /path/to/cover/art/image.png ``` ### Uploading Song with metadata from directory ```BASH -icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -smca /path/where/songs/and/metadata/exists/ +icarus-dm upload-meta -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -smca /path/where/songs/and/metadata/exists/ ``` ### Retrieving Song in json ```Bash -icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -rt songs +icarus-dm retrieve -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -rt songs ``` ### Deleting Song ```BASH -icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -D e8407fc6-edd2-44c1-993f-08dd7324d91a +icarus-dm delete -u spacecadet -p stellar40 -h https://icarus.com -ha https://auth.icarus.com -D e8407fc6-edd2-44c1-993f-08dd7324d91a ``` diff --git a/src/help.rs b/src/help.rs index 49ba02f..fb6d2ce 100644 --- a/src/help.rs +++ b/src/help.rs @@ -12,7 +12,8 @@ pub fn print_help() { Required for all actions -u username -p password - -h host + -h api host + -ha auth api host Required for upload with metadata -s path of song @@ -32,5 +33,5 @@ pub fn print_help() { -D song id"#, ); - println!("{}", msg); + println!("{msg}"); } diff --git a/src/main.rs b/src/main.rs index c47d149..98eb30d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,7 +7,8 @@ mod utilities; use std::env; -fn main() { +#[tokio::main] +async fn main() { let args: Vec = env::args().collect(); let args_len = args.len() as i32; @@ -16,7 +17,7 @@ fn main() { utilities::checks::exit_program(-1); } - println!("Argument count: {}", args_len); + println!("Argument count: {args_len}"); let mut act_mgr = managers::action_managers::ActionManager::default(); act_mgr.set_params(&args); @@ -29,7 +30,7 @@ fn main() { ica_action: chosen_act, }; - cmt_mgr.commit_action(); + cmt_mgr.commit_action().await; } #[cfg(test)] @@ -41,7 +42,7 @@ mod tests { let meta_path = String::from("tests/sample2_tracks/album.json"); if !std::path::Path::new(&meta_path).exists() { - assert!(false, "File does not exists: {:?}", meta_path); + assert!(false, "File does not exists: {meta_path:?}"); } match icarus_models::album::collection::parse_album(&meta_path) { diff --git a/src/managers/action_managers.rs b/src/managers/action_managers.rs index 42ed6ca..680981c 100644 --- a/src/managers/action_managers.rs +++ b/src/managers/action_managers.rs @@ -23,18 +23,19 @@ impl Default for ActionManager { impl ActionManager { pub fn retrieve_icarus_action(&self) -> models::icarus_action::IcarusAction { - return models::icarus_action::IcarusAction { + models::icarus_action::IcarusAction { flags: self.flags.clone(), action: String::from(&self.action), - }; + } } fn supported_flags(&self) -> Vec { - return vec![ + vec![ String::from("-u"), String::from("-p"), String::from("-t"), String::from("-h"), + String::from("-ha"), String::from("-s"), String::from("-sd"), String::from("-sr"), @@ -47,7 +48,7 @@ impl ActionManager { String::from("-ca"), String::from("-smca"), String::from("-t"), - ]; + ] } pub fn initialize(&mut self) { @@ -56,8 +57,8 @@ impl ActionManager { self.action = self.action.to_lowercase(); } - pub fn set_params(&mut self, args: &Vec) { - self.params = args.clone(); + pub fn set_params(&mut self, args: &[String]) { + self.params = args.to_owned(); self.param_count = self.params.len() as i32; } @@ -71,26 +72,26 @@ impl ActionManager { while i < flag_vals.len() { let flag = &flag_vals[i]; - println!("Index: {} | Value: {}", i, flag); + println!("Index: {i} | Value: {flag}"); let mut flg = models::flags::Flags::default(); + // TODO: Refactor this if self.is_valid_flag(flag) && self.does_flag_have_value(flag) { println!("Flag has value"); flg.flag = String::from(flag); flg.value = String::from(&flag_vals[i + 1]); - i = i + 1; + i += 1; } else if self.is_valid_flag(flag) { println!("Flag does not have a value"); flg.flag = String::from(flag); } else { - println!("Flag {} is not valid", flag); + println!("Flag {flag} is not valid"); utilities::checks::exit_program(-1); } self.flags.push(flg); - println!(""); i += 1; } } @@ -113,34 +114,30 @@ impl ActionManager { } } - return found; + found } fn does_flag_have_value(&self, flag: &String) -> bool { let flags_tmp = self.parsed_flags(); let mut i_found: i32 = -1; - for i in 0..flags_tmp.len() { - let flg = &flags_tmp[i]; - if flg == flag { + for (i, item) in flags_tmp.iter().enumerate() { + let flg = &item; + if *flg == flag { i_found = i as i32; break; } } if i_found >= 0 { - if (i_found + 1) < flags_tmp.len().try_into().unwrap() { - return true; - } else { - return false; - } + (i_found + 1) < flags_tmp.len().try_into().unwrap() } else { - return false; + false } } fn _print_action(&self) { - if self.action.len() == 0 { + if self.action.is_empty() { println!("Action is empty"); } else { println!("Action is {}", self.action); @@ -163,6 +160,6 @@ impl ActionManager { parsed.push(flag); } - return parsed; + parsed } } diff --git a/src/managers/commit_manager.rs b/src/managers/commit_manager.rs index 8357384..94d7e12 100644 --- a/src/managers/commit_manager.rs +++ b/src/managers/commit_manager.rs @@ -5,7 +5,6 @@ use std::io::{Result, Write}; use std::str::FromStr; use serde::{Deserialize, Serialize}; -use tokio::runtime::Runtime; use crate::managers; use crate::models::{self}; @@ -23,7 +22,6 @@ enum ActionValues { DeleteAct, DownloadAct, RetrieveAct, - UploadAct, UploadSongWithMetadata, None, } @@ -36,12 +34,20 @@ enum En { Other, } +#[derive(Debug)] +struct UploadSongMembers { + pub song: icarus_models::song::Song, + pub coverart: icarus_models::coverart::CoverArt, + pub token: icarus_models::token::AccessToken, + pub album: icarus_models::album::collection::Album, +} + pub fn retrieve_song( album: &icarus_models::album::collection::Album, track: i32, disc: i32, - directory: &String, - filename: &String, + directory: &str, + filename: &str, ) -> Result { let mut found = false; let mut song = icarus_models::song::Song::default(); @@ -55,16 +61,16 @@ pub fn retrieve_song( song.audio_type = String::from( icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION, ); - song.disc = track.disc.clone(); - song.disc_count = album.disc_count.clone(); + song.disc = track.disc; + song.disc_count = album.disc_count; song.duration = track.duration as i32; song.genre = album.genre.clone(); song.title = track.title.clone(); - song.year = album.year.clone(); - song.track = track.track.clone(); - song.track_count = album.track_count.clone(); - song.directory = directory.clone(); - song.filename = filename.clone(); + song.year = album.year; + song.track = track.track; + song.track_count = album.track_count; + song.directory = directory.to_owned(); + song.filename = filename.to_owned(); found = true; break; @@ -72,31 +78,29 @@ pub fn retrieve_song( } if found { - return Ok(song); + Ok(song) + } else { + Err(std::io::Error::other("Song not found")) } - - return Err(std::io::Error::new( - std::io::ErrorKind::NotFound, - "Song not found", - )); } impl CommitManager { - pub fn commit_action(&mut self) { + pub async fn commit_action(&mut self) { let action = &self.ica_action.action; - println!("Committing {} action", action); + println!("Committing {action} action"); let mapped_actions = &self.map_actions(); - let mapped_action = self.find_mapped_action(&mapped_actions, action); + let mapped_action = self.find_mapped_action(mapped_actions, action); - println!("{:?}", mapped_action); + println!("{mapped_action:?}"); + + // TODO: Move code to get token here and then pass it to the respective functions match mapped_action { - ActionValues::DeleteAct => self.delete_song(), - ActionValues::DownloadAct => self.download_song(), - ActionValues::RetrieveAct => self.retrieve_object(), - ActionValues::UploadAct => self.upload_song(), - ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata(), + ActionValues::DeleteAct => self.delete_song().await, + ActionValues::DownloadAct => self.download_song().await, + ActionValues::RetrieveAct => self.retrieve_object().await, + ActionValues::UploadSongWithMetadata => self.upload_song_with_metadata().await, _ => { println!("Nothing good here"); } @@ -114,14 +118,13 @@ impl CommitManager { } } - return ActionValues::None; + ActionValues::None } fn map_actions(&self) -> HashMap { let actions: HashMap = HashMap::from([ ("download".to_string(), ActionValues::DownloadAct), ("download".to_string(), ActionValues::DownloadAct), - ("upload".to_string(), ActionValues::UploadAct), ( "upload-meta".to_string(), ActionValues::UploadSongWithMetadata, @@ -130,18 +133,19 @@ impl CommitManager { ("delete".to_string(), ActionValues::DeleteAct), ]); - return actions; + actions } - fn delete_song(&self) { + async fn delete_song(&self) { let mut prsr = parsers::api_parser::APIParser { + apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), - api: models::api::API::default(), }; - prsr.parse_api(); - let api = prsr.retrieve_api(); + prsr.parse_api(parsers::api_parser::APIType::Main); + prsr.parse_api(parsers::api_parser::APIType::Auth); + let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); - let token = self.parse_token(&api); + let token = self.parse_token(&auth_api).await; println!("Deleting song"); @@ -156,86 +160,96 @@ impl CommitManager { } } + let api = prsr.retrieve_api(parsers::api_parser::APIType::Main); let mut del = syncers::delete::Delete { api: api.clone() }; println!("Deleting song.."); - let res_fut = del.delete_song(&token, &song); - let result = Runtime::new().unwrap().block_on(res_fut); - match result { - Ok(o) => { - println!("Song (Id {:?}) has been successfully deleted", o.id); + match del.delete_song(&token, &song).await { + Ok((song, _coverart)) => { + println!("Song (Id {:?}) has been successfully deleted", song.id); } Err(er) => { - println!("Error {:?}", er); + println!("Error {er:?}"); } } } - fn download_song(&self) { + async fn download_song(&self) { println!("Deleting song"); let dwn = self.ica_action.retrieve_flag_value(&String::from("-b")); - let id = uuid::Uuid::from_str(dwn.as_str()).unwrap(); + let song_id = uuid::Uuid::from_str(dwn.as_str()).unwrap(); let mut prsr = parsers::api_parser::APIParser { - api: models::api::API::default(), + apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), }; - prsr.parse_api(); + prsr.parse_api(parsers::api_parser::APIType::Main); + prsr.parse_api(parsers::api_parser::APIType::Auth); - let api = prsr.retrieve_api(); - let token = self.parse_token(&api); + let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); + let token = self.parse_token(&auth_api).await; println!("Message: {}", token.message); + let api = prsr.retrieve_api(parsers::api_parser::APIType::Main); + let mut dwn_loader = syncers::download::Download { api: api.clone() }; - let mut song = icarus_models::song::Song::default(); - song.id = id; - let result_fut = dwn_loader.download_song(&token, &song); - match Runtime::new().unwrap().block_on(result_fut) { + let song = icarus_models::song::Song { + id: song_id, + ..Default::default() + }; + + match dwn_loader.download_song(&token, &song).await { Ok(o) => { println!("Success"); let filename = String::from("audio") + icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION; let data = o.as_bytes(); let mut file = std::fs::File::create(filename).expect("Failed to save"); - file.write_all(&data) + file.write_all(data) .expect("Failed to save downloaded song"); } Err(er) => { - println!("Error {:?}", er); + println!("Error {er:?}"); match er { syncers::download::MyError::Request(error) => { - println!("Error: {:?}", error); + println!("Error: {error:?}"); } syncers::download::MyError::Other(ss) => { - println!("Error: {:?}", ss); + println!("Error: {ss:?}"); } } } } } - fn retrieve_object(&self) { + async fn retrieve_object(&self) { println!("Retrieving song"); let rt = self.ica_action.retrieve_flag_value(&String::from("-rt")); if rt != "songs" { - panic!("Unsupported -rt: {}", rt); + panic!("Unsupported -rt: {rt}"); } let mut prsr = parsers::api_parser::APIParser { - api: models::api::API::default(), + apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), }; - prsr.parse_api(); + prsr.parse_api(parsers::api_parser::APIType::Main); + prsr.parse_api(parsers::api_parser::APIType::Auth); + + let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); + let token = self.parse_token(&auth_api).await; + println!("Token {token:?}"); + + let api = prsr.retrieve_api(parsers::api_parser::APIType::Main); - let api = prsr.retrieve_api(); - let token = self.parse_token(&api); let mut repo = syncers::retrieve_records::RetrieveRecords { api: api.clone() }; - let result_fut = repo.get_all_songs(&token); - match Runtime::new().unwrap().block_on(result_fut) { + match repo.get_all_songs(&token).await { Ok(o) => { + println!("Songs"); + println!("====="); for song in o { println!("Title: {:?}", song.title); println!("Artist: {:?}", song.artist); @@ -244,17 +258,12 @@ impl CommitManager { } } Err(er) => { - println!("Error: {:?}", er); + println!("Error: {er:?}"); } } } - fn upload_song(&self) { - println!("Deleting song"); - panic!("Not supported"); - } - - fn parse_token(&self, api: &models::api::API) -> icarus_models::token::AccessToken { + async fn parse_token(&self, api: &models::api::Api) -> icarus_models::token::AccessToken { println!("Fetching token"); let mut usr_mgr: managers::user_manager::UserManager = @@ -271,12 +280,12 @@ impl CommitManager { }; tok_mgr.init(); - let token = Runtime::new().unwrap().block_on(tok_mgr.request_token()); + let token = tok_mgr.request_token().await; - return token.unwrap(); + token.unwrap() } - fn upload_song_with_metadata(&mut self) { + async fn upload_song_with_metadata(&mut self) { println!("Uplodaring song with metadara"); let songpath = self.ica_action.retrieve_flag_value(&String::from("-s")); @@ -284,13 +293,13 @@ impl CommitManager { let coverpath = self.ica_action.retrieve_flag_value(&String::from("-ca")); let track_id = self.ica_action.retrieve_flag_value(&String::from("-t")); - let single_target = songpath.len() > 0 - && metadata_path.len() > 0 - && coverpath.len() > 0 - && track_id.len() > 0; + let single_target = !songpath.is_empty() + && !metadata_path.is_empty() + && !coverpath.is_empty() + && !track_id.is_empty(); let uni = self.ica_action.retrieve_flag_value(&String::from("-smca")); - let multitarget = uni.len() > 0; + let multitarget = !uni.is_empty(); if single_target && multitarget { println!("Cannot upload from source and directory"); @@ -298,34 +307,38 @@ impl CommitManager { } if single_target { - println!("Song path: {}", songpath); - println!("Track ID: {}", track_id); - println!("metadata path: {}", metadata_path); - println!("cover art path: {}", coverpath); + println!("Song path: {songpath}"); + println!("Track ID: {track_id}"); + println!("metadata path: {metadata_path}"); + println!("cover art path: {coverpath}"); - let _ = self.sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath); + let _ = self + .sing_target_upload(&songpath, &track_id, &metadata_path, &coverpath) + .await; } else if multitarget { - let _ = self.multi_target_upload(&uni); + let _ = self.multi_target_upload(&uni).await; } else { println!("Single or Multi target has not been chosen"); } } - fn sing_target_upload( + /// Only uploading one song + async fn sing_target_upload( &mut self, songpath: &String, - track_id: &String, + track_id: &str, meta_path: &String, - cover_path: &String, + cover_path: &str, ) -> Result<()> { let mut prsr = parsers::api_parser::APIParser { - api: models::api::API::default(), + apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), }; - prsr.parse_api(); + prsr.parse_api(parsers::api_parser::APIType::Main); + prsr.parse_api(parsers::api_parser::APIType::Auth); - let api = prsr.retrieve_api(); - let token = self.parse_token(&api); + let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); + let token = self.parse_token(&auth_api).await; println!("Token: {:?}", token.token); @@ -339,17 +352,18 @@ impl CommitManager { let mut cover_art = icarus_models::coverart::CoverArt { id: uuid::Uuid::nil(), title: String::new(), - path: cover_path.clone(), + path: cover_path.to_owned(), data: Vec::new(), + ..Default::default() }; let file_name = std::ffi::OsString::from(&song_file.file_name().unwrap()); match self.find_file_extension(&file_name) { En::SongFile => match utilities::string::o_to_string(&file_name) { Ok(s) => { - println!("file name: {:?}", file_name); + println!("file name: {file_name:?}"); - match icarus_models::album::collection::parse_album(&meta_path) { + match icarus_models::album::collection::parse_album(meta_path) { Ok(album) => { let filename = s.clone(); let directory = song_file.parent().unwrap().display().to_string(); @@ -363,63 +377,115 @@ impl CommitManager { cover_art.data = cover_art.to_data().unwrap(); - let mut up = syncers::upload::Upload::default(); - let host = self.ica_action.retrieve_flag_value(&String::from("-h")); - up.set_api(&host); + let members = UploadSongMembers { + song: s, + coverart: cover_art, + token: token, + album: album, + }; - let res = up.upload_song_with_metadata(&token, &s, &cover_art); - - match Runtime::new().unwrap().block_on(res) { - Ok(o) => { - println!("Successfully sent {:?}", o); - Ok(()) - } - Err(er) => { - println!("Some error {:?}", er); - Err(std::io::Error::new( - std::io::ErrorKind::Other, - er.to_string(), - )) - } + match self.upload_song_process(&members).await { + Ok(_) => Ok(()), + Err(err) => Err(err), } } Err(err) => { - println!("Error: {:?}", err); - Err(std::io::Error::new( - std::io::ErrorKind::Other, - err.to_string(), - )) + println!("Error: {err:?}"); + Err(std::io::Error::other(err.to_string())) } } } Err(er) => { - println!("Error: {:?}", er); - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - er.to_string(), - )); + println!("Error: {er:?}"); + Err(std::io::Error::other(er.to_string())) } }, - _ => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "No sutitable file found".to_owned(), - )); + _ => Err(std::io::Error::other("No sutitable file found".to_owned())), + } + } + + /// Upload song to the queue to get processed + async fn upload_song_process(&self, data: &UploadSongMembers) -> Result<()> { + let mut up = syncers::upload::Upload::default(); + let host = self.ica_action.retrieve_flag_value(&String::from("-h")); + up.set_api(&host); + + let token = &data.token; + let song = &data.song; + let album = &data.album; + let coverart = &data.coverart; + + println!("Queueing song"); + + let queued_song_id = match up.queue_song(token, song).await { + Ok(id) => id, + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }; + + println!("Queued song Id: {queued_song_id:?}"); + + match up.link_user_to_queued_song(token, &queued_song_id).await { + Ok(_) => { + println!("Queued song linked to user"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); } } + + let queued_metadata_id = match up.queue_metadata(token, album, song, &queued_song_id).await + { + Ok(id) => id, + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }; + + println!("Queued metadata Id: {queued_metadata_id:?}"); + + match up.queue_coverart(token, coverart).await { + Ok(id) => match up + .link_queued_song_to_queued_coverart(token, &queued_song_id, &id) + .await + { + Ok(_) => match up + .update_queued_song_status(token, &queued_song_id, "ready") + .await + { + Ok(_) => { + println!("Queued coverart Id: {id:?}"); + println!("Linked queued song to queued coverart"); + println!("Queued status updated"); + } + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }, + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + }, + Err(err) => { + return Err(std::io::Error::other(err.to_string())); + } + } + + Ok(()) } fn get_songs( &self, metadata_path: &String, - source_directory: &String, + source_directory: &str, ) -> Result> { match icarus_models::album::collection::parse_album(metadata_path) { Ok(albums) => { let mut songs: Vec = Vec::new(); for track in &albums.tracks { - let filename = if track.track < 10 { + let song_filename = if track.track < 10 { "track0".to_owned() + &track.track.to_string() + icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION @@ -433,18 +499,18 @@ impl CommitManager { id: uuid::Uuid::nil(), title: track.title.clone(), artist: track.artist.clone(), - disc: track.disc.clone(), - track: track.track.clone(), - duration: track.duration.clone() as i32, - year: albums.year.clone(), + disc: track.disc, + track: track.track, + duration: track.duration as i32, + year: albums.year, album_artist: albums.artist.clone(), genre: albums.genre.clone(), - disc_count: albums.disc_count.clone(), - track_count: albums.track_count.clone(), + disc_count: albums.disc_count, + track_count: albums.track_count, album: albums.title.clone(), audio_type: String::from("FLAC"), - directory: source_directory.clone(), - filename: filename, + directory: source_directory.to_owned(), + filename: song_filename, user_id: uuid::Uuid::nil(), data: Vec::new(), date_created: String::new(), @@ -456,14 +522,15 @@ impl CommitManager { } } - fn multi_target_upload(&mut self, sourcepath: &String) -> std::io::Result<()> { + async fn multi_target_upload(&mut self, sourcepath: &String) -> std::io::Result<()> { let mut prsr = parsers::api_parser::APIParser { - api: models::api::API::default(), + apis: vec![models::api::Api::default(), models::api::Api::default()], ica_act: self.ica_action.clone(), }; - prsr.parse_api(); - let api = prsr.retrieve_api(); - let token = self.parse_token(&api); + prsr.parse_api(parsers::api_parser::APIType::Main); + prsr.parse_api(parsers::api_parser::APIType::Auth); + let auth_api = prsr.retrieve_api(parsers::api_parser::APIType::Auth); + let token = self.parse_token(&auth_api).await; let directory_path = std::path::Path::new(&sourcepath); @@ -471,16 +538,10 @@ impl CommitManager { panic!("Directory does not exist"); } - let coverart_path = match self.get_cover_art_path(&sourcepath) { - Ok(path) => path, - Err(_) => String::new(), - }; + let coverart_path = self.get_cover_art_path(sourcepath).unwrap_or_default(); let mut cover_art = icarus_models::coverart::init::init_coverart_only_path(coverart_path.clone()); - let metadatapath = match self.get_metadata_path(&sourcepath) { - Ok(o) => o, - Err(_) => String::new(), - }; + let metadatapath = self.get_metadata_path(sourcepath).unwrap_or_default(); let mut up = syncers::upload::Upload::default(); let host = self.ica_action.retrieve_flag_value(&String::from("-h")); @@ -488,30 +549,40 @@ impl CommitManager { cover_art.data = cover_art.to_data().unwrap(); - println!(""); + match self.get_songs(&metadatapath, sourcepath) { + Ok(sngs) => match icarus_models::album::collection::parse_album(&metadatapath) { + Ok(album) => { + for song in sngs { + let members = UploadSongMembers { + song: song, + coverart: cover_art.clone(), + token: token.clone(), + album: album.clone(), + }; - match self.get_songs(&metadatapath, &sourcepath) { - Ok(sngs) => { - for song in sngs { - match Runtime::new() - .unwrap() - .block_on(up.upload_song_with_metadata(&token, &song, &cover_art)) - { - Ok(o) => { - println!("Response: {:?}", o); + match self.upload_song_process(&members).await { + Ok(o) => { + println!("Response: {o:?}"); + } + Err(err) => { + println!("Error: {err:?}"); + return Err(err); + } } - Err(err) => { - println!("Error: {:?}", err); - } - }; + } + + Ok(()) } - } + Err(err) => { + println!("Error: {err:?}"); + Err(std::io::Error::other(err.to_string())) + } + }, Err(error) => { - println!("Error: {:?}", error); + println!("Error: {error:?}"); + Err(std::io::Error::other(error.to_string())) } } - - Ok(()) } fn get_cover_art_path(&self, directory_path: &String) -> Result { @@ -521,17 +592,14 @@ impl CommitManager { let file_type = entry.file_type(); let file_name = entry.file_name(); - println!("file type: {:?}", file_type); - println!("file name: {:?}", file_name); + println!("file type: {file_type:?}"); + println!("file name: {file_name:?}"); - match self.find_file_extension(&file_name) { - En::ImageFile => { - let directory_part = directory_path.clone(); - let fname = utilities::string::o_to_string(&file_name); - let fullpath = directory_part + "/" + &fname.unwrap(); - return Ok(fullpath); - } - _ => {} + if let En::ImageFile = self.find_file_extension(&file_name) { + let directory_part = directory_path.clone(); + let fname = utilities::string::o_to_string(&file_name); + let fullpath = format!("{}/{}", directory_part, &fname.unwrap()); + return Ok(fullpath); } } @@ -583,7 +651,7 @@ impl CommitManager { } } - return En::Other; + En::Other } fn get_metadata_path(&self, directory_path: &String) -> Result { @@ -593,16 +661,13 @@ impl CommitManager { let file_type = entry.file_type(); let file_name = entry.file_name(); - println!("file type: {:?}", file_type); - println!("file name: {:?}", file_name); + println!("file type: {file_type:?}"); + println!("file name: {file_name:?}"); - match self.find_file_extension(&file_name) { - En::MetadataFile => { - let directory_part = directory_path.clone(); - let fname = utilities::string::o_to_string(&file_name); - return Ok(directory_part + "/" + &fname.unwrap()); - } - _ => {} + if let En::MetadataFile = self.find_file_extension(&file_name) { + let directory_part = directory_path.clone(); + let fname = utilities::string::o_to_string(&file_name); + return Ok(format!("{}/{}", directory_part, &fname.unwrap())); } } @@ -615,6 +680,6 @@ impl CommitManager { return true; } } - return false; + false } } diff --git a/src/managers/token_manager.rs b/src/managers/token_manager.rs index b66b6b8..a539f0e 100644 --- a/src/managers/token_manager.rs +++ b/src/managers/token_manager.rs @@ -2,21 +2,31 @@ use std::default::Default; use crate::models; +mod response { + pub mod token { + #[derive(Debug, serde::Deserialize, serde::Serialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } +} + pub struct TokenManager { pub user: icarus_models::user::User, - pub api: models::api::API, + pub api: models::api::Api, } impl Default for TokenManager { fn default() -> Self { let mut token = TokenManager { user: icarus_models::user::User::default(), - api: models::api::API::default(), + api: models::api::Api::default(), }; token.init(); - return token; + token } } @@ -26,7 +36,7 @@ impl TokenManager { let url = self.retrieve_url(); - println!("URL: {}", url); + println!("URL: {url}"); let mut token = icarus_models::token::AccessToken { user_id: uuid::Uuid::nil(), @@ -43,11 +53,15 @@ impl TokenManager { match response.status() { reqwest::StatusCode::OK => { // on success, parse our JSON to an APIResponse - let s = response.json::().await; - match s { - // - Ok(parsed) => { - token = parsed; + match response.json::().await { + Ok(response) => { + let login_result = &response.data[0]; + token.user_id = login_result.id; + token.username = login_result.username.clone(); + token.token = login_result.token.clone(); + token.token_type = login_result.token_type.clone(); + token.expiration = login_result.expiration; + token.message = response.message; } Err(_) => println!("Hm, the response didn't match the shape we expected."), }; @@ -56,17 +70,17 @@ impl TokenManager { println!("Need to grab a new token"); } other => { - panic!("Uh oh! Something unexpected happened: {:?}", other); + panic!("Uh oh! Something unexpected happened: {other:?}"); } } - return Ok(token); + Ok(token) } pub fn init(&mut self) { let api = &mut self.api; - api.version = String::from("v1"); - api.endpoint = String::from(format!("api/{}/login", api.version)); + api.version = String::from(crate::parsers::api_parser::API_VERSION); + api.endpoint = format!("api/{}/login", api.version); } pub fn retrieve_url(&self) -> String { @@ -74,6 +88,6 @@ impl TokenManager { let mut url = String::from(&api.url); url += &String::from(&api.endpoint); - return url; + url } } diff --git a/src/managers/user_manager.rs b/src/managers/user_manager.rs index cf52bd8..d393eac 100644 --- a/src/managers/user_manager.rs +++ b/src/managers/user_manager.rs @@ -4,24 +4,15 @@ use serde::{Deserialize, Serialize}; use crate::models::{self}; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct UserManager { pub user: icarus_models::user::User, pub ica_action: models::icarus_action::IcarusAction, } -impl Default for UserManager { - fn default() -> Self { - UserManager { - user: icarus_models::user::User::default(), - ica_action: models::icarus_action::IcarusAction::default(), - } - } -} - impl UserManager { pub fn retrieve_user(&self) -> icarus_models::user::User { - return self.user.clone(); + self.user.clone() } pub fn parse_user_from_actions(&mut self) { diff --git a/src/models/api.rs b/src/models/api.rs index aeb9269..6f64ddd 100644 --- a/src/models/api.rs +++ b/src/models/api.rs @@ -2,19 +2,9 @@ use std::default::Default; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct API { +#[derive(Clone, Debug, Default, Deserialize, Serialize)] +pub struct Api { pub url: String, pub endpoint: String, pub version: String, } - -impl Default for API { - fn default() -> Self { - API { - url: String::new(), - endpoint: String::new(), - version: String::new(), - } - } -} diff --git a/src/models/flags.rs b/src/models/flags.rs index d51f2b3..7258812 100644 --- a/src/models/flags.rs +++ b/src/models/flags.rs @@ -2,17 +2,8 @@ use std::default::Default; use serde::{Deserialize, Serialize}; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct Flags { pub flag: String, pub value: String, } - -impl Default for Flags { - fn default() -> Self { - Flags { - flag: String::new(), - value: String::new(), - } - } -} diff --git a/src/models/icarus_action.rs b/src/models/icarus_action.rs index 4ef6aad..3de451b 100644 --- a/src/models/icarus_action.rs +++ b/src/models/icarus_action.rs @@ -4,21 +4,12 @@ use serde::{Deserialize, Serialize}; use crate::models; -#[derive(Clone, Debug, Deserialize, Serialize)] +#[derive(Clone, Debug, Default, Deserialize, Serialize)] pub struct IcarusAction { pub action: String, pub flags: Vec, } -impl Default for IcarusAction { - fn default() -> Self { - IcarusAction { - action: String::new(), - flags: Vec::new(), - } - } -} - impl IcarusAction { pub fn retrieve_flag_value(&self, flag: &String) -> String { let mut val: String = String::new(); @@ -30,7 +21,7 @@ impl IcarusAction { } } - return val; + val } pub fn print_action_and_flags(&self) { diff --git a/src/models/upload_form.rs b/src/models/upload_form.rs index ef90173..335481c 100644 --- a/src/models/upload_form.rs +++ b/src/models/upload_form.rs @@ -2,17 +2,8 @@ use std::default::Default; use serde::{Deserialize, Serialize}; -#[derive(Debug, Deserialize, Serialize)] +#[derive(Debug, Default, Deserialize, Serialize)] pub struct UploadForm { pub url: Option, pub filepath: Option, } - -impl Default for UploadForm { - fn default() -> Self { - UploadForm { - url: None, - filepath: None, - } - } -} diff --git a/src/parsers/api_parser.rs b/src/parsers/api_parser.rs index 7a8396f..61b86dc 100644 --- a/src/parsers/api_parser.rs +++ b/src/parsers/api_parser.rs @@ -2,16 +2,26 @@ use crate::models; #[derive(Clone, Debug)] pub struct APIParser { - pub api: models::api::API, + pub apis: Vec, pub ica_act: models::icarus_action::IcarusAction, } +pub const API_VERSION: &str = "v2"; + +pub enum APIType { + Main, + Auth, +} + impl APIParser { - pub fn retrieve_api(&self) -> models::api::API { - return self.api.clone(); + pub fn retrieve_api(&self, api_type: APIType) -> models::api::Api { + match api_type { + APIType::Main => self.apis[0].clone(), + APIType::Auth => self.apis[1].clone(), + } } - pub fn parse_api(&mut self) { + pub fn parse_api(&mut self, api_type: APIType) { let flags = self.ica_act.flags.clone(); println!("Parsing api"); @@ -19,16 +29,32 @@ impl APIParser { let arg = elem.flag; let value = elem.value; - if arg == "-h" { - if value.chars().nth(value.len() - 1) == Some('/') { - self.api.url = value; - } else { - self.api.url = value + "/"; + match api_type { + APIType::Main => { + if arg == "-h" { + if value.chars().nth(value.len() - 1) == Some('/') { + self.apis[0].url = value; + } else { + self.apis[0].url = format!("{value}/"); + } + break; + } + } + APIType::Auth => { + if arg == "-ha" { + if value.chars().nth(value.len() - 1) == Some('/') { + self.apis[1].url = value; + } else { + self.apis[1].url = format!("{value}/"); + } + break; + } } - break; } } - self.api.version = "v1".to_string(); + for api in &mut self.apis { + api.version = String::from(API_VERSION); + } } } diff --git a/src/syncers/common.rs b/src/syncers/common.rs index 59933ca..095b39b 100644 --- a/src/syncers/common.rs +++ b/src/syncers/common.rs @@ -1,33 +1,30 @@ use crate::models; -pub fn retrieve_url(api: &models::api::API, with_id: bool, id: &uuid::Uuid) -> String { +pub fn retrieve_url(api: &models::api::Api, with_id: bool, id: &uuid::Uuid) -> String { if with_id { - retrieve_url_with_id(&api, id) + retrieve_url_with_id(api, id) } else { - retrieve_url_reg(&api) + retrieve_url_reg(api) } } -fn retrieve_url_reg(api: &models::api::API) -> String { - let mut url: String = String::from(&api.url); - url += &String::from("/"); - url += &String::from("api/"); - url += &String::from(&api.version); - url += &String::from("/"); - url += &String::from(&api.endpoint); - url += &String::from("/"); +fn retrieve_url_reg(api: &models::api::Api) -> String { + let url = format!("{}api/{}/{}/", api.url, api.version, api.endpoint); - return url; + url } -fn retrieve_url_with_id(api: &models::api::API, id: &uuid::Uuid) -> String { - let mut url: String = String::from(&api.url); - url += &String::from("api/"); - url += &String::from(&api.version); - url += &String::from("/"); - url += &String::from(&api.endpoint); - url += &String::from("/"); - url += &id.to_string(); +fn retrieve_url_with_id(api: &models::api::Api, id: &uuid::Uuid) -> String { + let url = format!("{}api/{}/{}/{}", api.url, api.version, api.endpoint, id); - return url; + url +} + +pub async fn auth_header( + token: &icarus_models::token::AccessToken, +) -> (http::HeaderName, http::HeaderValue) { + let auth = reqwest::header::AUTHORIZATION; + let auth_value = http::HeaderValue::from_str(&token.bearer_token()).unwrap(); + + (auth, auth_value) } diff --git a/src/syncers/delete.rs b/src/syncers/delete.rs index a62f24b..2531f03 100644 --- a/src/syncers/delete.rs +++ b/src/syncers/delete.rs @@ -3,17 +3,24 @@ use std::default::Default; use reqwest; use crate::models; -use crate::syncers; -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Default)] pub struct Delete { - pub api: models::api::API, + pub api: models::api::Api, } -impl Default for Delete { - fn default() -> Self { - Delete { - api: models::api::API::default(), +mod response { + pub mod delete_song { + #[derive(Debug, serde::Deserialize)] + pub struct SongAndCoverArt { + pub song: icarus_models::song::Song, + pub coverart: icarus_models::coverart::CoverArt, + } + + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, } } } @@ -23,9 +30,12 @@ impl Delete { &mut self, token: &icarus_models::token::AccessToken, song: &icarus_models::song::Song, - ) -> Result { - self.api.endpoint = "song/data/delete".to_owned(); - let url = syncers::common::retrieve_url(&self.api, true, &song.id); + ) -> Result<(icarus_models::song::Song, icarus_models::coverart::CoverArt), std::io::Error> + { + self.api.endpoint = "api/v2/song".to_owned(); + let url = format!("{}{}/{}", self.api.url, self.api.endpoint, song.id); + println!("Url: {url:?}"); + let client = reqwest::Client::builder().build().unwrap(); let access_token = token.bearer_token(); let response = client @@ -39,18 +49,16 @@ impl Delete { reqwest::StatusCode::OK => { println!("Success!"); - match response.json::().await { - Ok(sng) => Ok(sng), - Err(er) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - er.to_string(), - )), + match response.json::().await { + Ok(resp) => { + println!("Response message: {:?}", resp.message); + let data = &resp.data[0]; + Ok((data.song.clone(), data.coverart.clone())) + } + Err(er) => Err(std::io::Error::other(er.to_string())), } } - other => Err(std::io::Error::new( - std::io::ErrorKind::Other, - other.to_string(), - )), + other => Err(std::io::Error::other(other.to_string())), } } } diff --git a/src/syncers/download.rs b/src/syncers/download.rs index 7e92bc8..f5da441 100644 --- a/src/syncers/download.rs +++ b/src/syncers/download.rs @@ -3,16 +3,9 @@ use std::default::Default; use crate::models; use crate::syncers; +#[derive(Default)] pub struct Download { - pub api: models::api::API, -} - -impl Default for Download { - fn default() -> Self { - Download { - api: models::api::API::default(), - } - } + pub api: models::api::Api, } #[derive(Debug)] @@ -27,11 +20,11 @@ impl Download { token: &icarus_models::token::AccessToken, song: &icarus_models::song::Song, ) -> Result { - self.api.endpoint = String::from("song/data/download"); + self.api.endpoint = String::from("song/download"); let url = syncers::common::retrieve_url(&self.api, true, &song.id); let access_token = token.bearer_token(); - println!("Url: {:?}", url); + println!("Url: {url:?}"); let client = reqwest::Client::builder().build().unwrap(); @@ -42,27 +35,16 @@ impl Download { .await { Ok(rep) => match rep.status() { - reqwest::StatusCode::OK => { - let data = rep.text(); - match data.await { - Ok(e) => { - return Ok(e); - } - Err(er) => { - return Err(MyError::Other(er.to_string())); - } - } - } + reqwest::StatusCode::OK => match rep.text().await { + Ok(e) => Ok(e), + Err(er) => Err(MyError::Other(er.to_string())), + }, reqwest::StatusCode::UNAUTHORIZED => { - return Err(MyError::Other(String::from("Need to grab a new token"))); - } - other => { - return Err(MyError::Other(other.to_string())); + Err(MyError::Other(String::from("Need to grab a new token"))) } + other => Err(MyError::Other(other.to_string())), }, - Err(er) => { - return Err(MyError::Request(er)); - } + Err(er) => Err(MyError::Request(er)), } } } diff --git a/src/syncers/retrieve_records.rs b/src/syncers/retrieve_records.rs index f018337..96752c0 100644 --- a/src/syncers/retrieve_records.rs +++ b/src/syncers/retrieve_records.rs @@ -2,16 +2,18 @@ use std::default::Default; use std::io::Error; use crate::models; -use crate::syncers; +#[derive(Default)] pub struct RetrieveRecords { - pub api: models::api::API, + pub api: models::api::Api, } -impl Default for RetrieveRecords { - fn default() -> Self { - RetrieveRecords { - api: models::api::API::default(), +mod response { + pub mod get_all_songs { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, } } } @@ -21,10 +23,12 @@ impl RetrieveRecords { &mut self, token: &icarus_models::token::AccessToken, ) -> Result, Error> { - self.api.endpoint = String::from("song"); - let url = syncers::common::retrieve_url(&self.api, false, &uuid::Uuid::nil()); + self.api.endpoint = String::from("api/v2/song/all"); + let url = format!("{}{}", self.api.url, self.api.endpoint); let access_token = token.bearer_token(); + println!("url: {url:?}"); + let client = reqwest::Client::builder().build().unwrap(); let response = client .get(&url) @@ -35,29 +39,19 @@ impl RetrieveRecords { match response.status() { reqwest::StatusCode::OK => { - // on success, parse our JSON to an APIResponse - match response.json::>().await { - Ok(parsed) => Ok(parsed), - Err(err) => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - err.to_string(), - )); + // on success, parse our JSON to an API Response + match response.json::().await { + Ok(parsed) => { + println!("Response message: {:?}", parsed.message); + Ok(parsed.data) } + Err(err) => Err(std::io::Error::other(err.to_string())), } } reqwest::StatusCode::UNAUTHORIZED => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Need to grab a new token", - )); - } - other => { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - other.to_string(), - )); + Err(std::io::Error::other("Need to grab a new token")) } + other => Err(std::io::Error::other(other.to_string())), } } } diff --git a/src/syncers/upload.rs b/src/syncers/upload.rs index f4a6e7d..0ecdf53 100644 --- a/src/syncers/upload.rs +++ b/src/syncers/upload.rs @@ -6,46 +6,72 @@ use reqwest; use crate::models; use crate::syncers; +#[derive(Default)] pub struct Upload { - pub api: models::api::API, + pub api: models::api::Api, } -impl Default for Upload { - fn default() -> Self { - Upload { - api: models::api::API::default(), +mod response { + pub mod queue_song { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod queue_metadata { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, + } + } + + pub mod queue_coverart { + #[derive(Debug, serde::Deserialize)] + pub struct Response { + pub message: String, + pub data: Vec, } } } impl Upload { - pub async fn upload_song_with_metadata( - &mut self, + pub async fn queue_song( + &self, token: &icarus_models::token::AccessToken, song: &icarus_models::song::Song, - cover: &icarus_models::coverart::CoverArt, - ) -> Result { - self.api.endpoint = String::from("song/data/upload/with/data"); - let url = syncers::common::retrieve_url(&self.api, false, &uuid::Uuid::nil()); - let access_token = token.bearer_token(); + ) -> Result { + let songpath = song.song_path().unwrap(); + let file = tokio::fs::File::open(&songpath).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let file_body = reqwest::Body::wrap_stream(stream); - if url.is_empty() { - println!("Url is empty"); - } + let form = reqwest::multipart::Form::new().part( + "file", + reqwest::multipart::Part::stream(file_body) + .file_name(song.filename.clone()) + .mime_str("application/octet-stream") + .unwrap(), + ); - println!("Url: {}", url); - println!("Token: {}", access_token); - println!("Path: {:?}", song.song_path()); + let endpoint = String::from("api/v2/song/queue"); + let url = format!("{}/{endpoint}", self.api.url); + println!("Url: {url:?}"); let mut headers = reqwest::header::HeaderMap::new(); - headers.insert( - reqwest::header::AUTHORIZATION, - HeaderValue::from_str(&access_token.clone()).unwrap(), - ); - headers.insert(reqwest::header::ACCEPT, HeaderValue::from_static("*/*")); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + headers.insert("Accept", HeaderValue::from_str("*/*").unwrap()); + headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap()); + headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap()); - let form = self.init_form(&song, &cover); - let client = reqwest::Client::builder().build().unwrap(); + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(300)) + .pool_idle_timeout(std::time::Duration::from_secs(30)) + .build() + .unwrap(); match client .post(url) .headers(headers) @@ -53,58 +79,226 @@ impl Upload { .send() .await { - Ok(r) => { - return Ok(r); - } - Err(err) => { - return Err(err); - } + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), } } - fn init_form( + pub async fn link_user_to_queued_song( &self, - song: &icarus_models::song::Song, - cover: &icarus_models::coverart::CoverArt, - ) -> reqwest::multipart::Form { - let songpath = match song.song_path() { - Ok(s) => s, - Err(_) => String::new(), - }; - let coverpath = cover.path.clone(); - println!("Cover path: {:?}", coverpath); - let song_detail = match song.to_metadata_json(true) { - Ok(s) => s, - Err(_) => String::new(), - }; + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/song/queue/link"); + let url = format!("{}/{endpoint}", self.api.url); - println!("\n{}\n", song_detail); + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); - let mut song_filename = String::from("audio"); - song_filename += icarus_models::constants::file_extensions::audio::DEFAULTMUSICEXTENSION; - let mut cover_filename = String::from("cover"); - cover_filename += icarus_models::constants::file_extensions::image::JPGEXTENSION; + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "user_id": token.user_id + }); - let form = reqwest::multipart::Form::new() - .part( - "file", - reqwest::multipart::Part::bytes(std::fs::read(songpath).unwrap()) - .file_name(song_filename), - ) - .part( - "cover", - reqwest::multipart::Part::bytes(std::fs::read(coverpath).unwrap()) - .file_name(cover_filename), - ) - .text("metadata", song_detail); + let client = reqwest::Client::builder().build().unwrap(); - return form; + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } } - pub fn set_api(&mut self, host: &String) { - let mut api = models::api::API::default(); - api.url = host.clone(); - api.version = String::from("v1"); + pub async fn queue_metadata( + &self, + token: &icarus_models::token::AccessToken, + album: &icarus_models::album::collection::Album, + song: &icarus_models::song::Song, + queued_song_id: &uuid::Uuid, + ) -> Result { + let endpoint = String::from("api/v2/song/metadata/queue"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "title": song.title, + "artist": song.artist, + "album_artist": album.artist, + "album": album.title, + "genre": song.genre, + "track": song.track, + "track_count": album.track_count, + "disc": song.disc, + "disc_count": album.disc_count, + "year": album.year, + "duration": song.duration, + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .post(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } + + pub async fn queue_coverart( + &self, + token: &icarus_models::token::AccessToken, + coverart: &icarus_models::coverart::CoverArt, + ) -> Result { + let coverartpath = coverart.path.clone(); + let file = tokio::fs::File::open(&coverartpath).await.unwrap(); + let stream = tokio_util::codec::FramedRead::new(file, tokio_util::codec::BytesCodec::new()); + let file_body = reqwest::Body::wrap_stream(stream); + + let file_name = std::path::Path::new(&coverartpath) + .file_name() + .and_then(|name| name.to_str()) + .map(String::from) + .unwrap_or("file".to_string()); + + let form = reqwest::multipart::Form::new().part( + "file", + reqwest::multipart::Part::stream(file_body) + .file_name(file_name) + .mime_str("application/octet-stream") + .unwrap(), + ); + + let endpoint = String::from("api/v2/coverart/queue"); + let url = format!("{}/{endpoint}", self.api.url); + println!("Url: {url:?}"); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + headers.insert("Accept", HeaderValue::from_str("*/*").unwrap()); + headers.insert("Connection", HeaderValue::from_str("keep-alive").unwrap()); + headers.insert("Cache-Control", HeaderValue::from_str("no-cache").unwrap()); + + let client = reqwest::Client::builder() + .timeout(std::time::Duration::from_secs(300)) + .pool_idle_timeout(std::time::Duration::from_secs(30)) + .build() + .unwrap(); + match client + .post(url) + .headers(headers) + .multipart(form) + .send() + .await + { + Ok(response) => match response.json::().await { + Ok(resp) => { + println!("Message: {:?}", resp.message); + Ok(resp.data[0]) + } + Err(err) => Err(err), + }, + Err(err) => Err(err), + } + } + + pub async fn link_queued_song_to_queued_coverart( + &self, + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + queued_coverart_id: &uuid::Uuid, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/coverart/queue/link"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "song_queue_id": queued_song_id, + "coverart_id": queued_coverart_id + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + pub async fn update_queued_song_status( + &self, + token: &icarus_models::token::AccessToken, + queued_song_id: &uuid::Uuid, + status: &str, + ) -> Result<(), reqwest::Error> { + let endpoint = String::from("api/v2/song/queue"); + let url = format!("{}/{endpoint}", self.api.url); + + let mut headers = reqwest::header::HeaderMap::new(); + let (auth, auth_val) = syncers::common::auth_header(token).await; + headers.insert(auth, auth_val); + + let payload = serde_json::json!({ + "id": queued_song_id, + "status": status + }); + + let client = reqwest::Client::builder().build().unwrap(); + + match client + .patch(url) + .headers(headers) + .json(&payload) + .send() + .await + { + Ok(_) => Ok(()), + Err(err) => Err(err), + } + } + + pub fn set_api(&mut self, host: &str) { + let api = models::api::Api { + url: host.to_owned(), + version: String::from(crate::parsers::api_parser::API_VERSION), + endpoint: String::new(), + }; self.api = api; } } diff --git a/src/utilities/checks.rs b/src/utilities/checks.rs index 76f69a5..e3d53a5 100644 --- a/src/utilities/checks.rs +++ b/src/utilities/checks.rs @@ -4,11 +4,11 @@ use serde::{Deserialize, Serialize}; pub struct Checks {} impl Checks { - pub fn _is_numeric(text: &String) -> bool { + pub fn _is_numeric(text: &str) -> bool { text.parse::().is_ok() } - pub fn _index_of_item_in_container(container: &String, item: &char, func: F) -> i32 + pub fn _index_of_item_in_container(container: &str, item: &char, func: F) -> i32 where F: Fn(&char, &char) -> bool, { diff --git a/src/utilities/string.rs b/src/utilities/string.rs index a5e089c..4a2bb63 100644 --- a/src/utilities/string.rs +++ b/src/utilities/string.rs @@ -1,9 +1,6 @@ pub fn o_to_string(val: &std::ffi::OsString) -> Result { match val.clone().into_string() { Ok(value) => Ok(value), - Err(_) => Err(std::io::Error::new( - std::io::ErrorKind::Other, - String::from("Error"), - )), + Err(_) => Err(std::io::Error::other(String::from("Error"))), } }