From b73bf418c519305050b6ee80819dd4a95a33ec10 Mon Sep 17 00:00:00 2001 From: daimond113 <72147841+daimond113@users.noreply.github.com> Date: Sat, 13 Jul 2024 00:09:37 +0200 Subject: [PATCH] feat: begin rewrite --- Cargo.lock | 4728 ++++++----------- Cargo.toml | 72 +- README.md | 22 +- registry/.env.example | 11 - registry/Cargo.toml | 33 - registry/src/endpoints/mod.rs | 2 - registry/src/endpoints/packages.rs | 256 - registry/src/endpoints/search.rs | 81 - registry/src/errors.rs | 77 - registry/src/main.rs | 316 -- src/cli/api_token.rs | 162 - src/cli/auth.rs | 111 - src/cli/auth/login.rs | 181 + src/cli/auth/logout.rs | 16 + src/cli/auth/mod.rs | 49 + src/cli/auth/whoami.rs | 26 + src/cli/config.rs | 42 - src/cli/config/default_index.rs | 39 + src/cli/config/mod.rs | 18 + src/cli/mod.rs | 382 +- src/cli/root.rs | 593 --- src/dependencies/git.rs | 237 - src/dependencies/mod.rs | 443 -- src/dependencies/registry.rs | 148 - src/dependencies/resolution.rs | 596 --- src/dependencies/wally.rs | 365 -- src/index.rs | 753 --- src/lib.rs | 199 +- src/linking_file.rs | 317 -- src/lockfile.rs | 20 + src/main.rs | 53 +- src/manifest.rs | 465 +- src/multithread.rs | 47 - src/names.rs | 95 + src/package_name.rs | 423 -- src/patches.rs | 214 - src/project.rs | 326 -- src/source/mod.rs | 79 + src/source/pesde.rs | 588 ++ tests/prelude.rs | 120 - tests/resolver.rs | 141 - website/.eslintignore | 9 - website/.eslintrc.cjs | 31 - website/.gitignore | 10 - website/.npmrc | 1 - website/.prettierignore | 4 - website/.prettierrc | 17 - website/bun.lockb | Bin 151456 -> 0 bytes website/package.json | 59 - .../simple-svelte-autocomplete@2.5.2.patch | 16 - website/patches/tar-stream@3.1.7.patch | 9 - website/postcss.config.js | 6 - website/src/app.css | 19 - website/src/app.d.ts | 13 - website/src/app.html | 23 - website/src/autocomplete.d.ts | 92 - website/src/lib/Codeblock.svelte | 14 - website/src/lib/markdown.ts | 18 - website/src/routes/+layout.svelte | 132 - website/src/routes/+page.svelte | 60 - website/src/routes/+page.ts | 26 - website/src/routes/docs/+page.svelte | 258 - website/src/routes/docs/Note.svelte | 10 - .../[scope]/[name]/[version]/+page.svelte | 199 - .../[scope]/[name]/[version]/+page.ts | 133 - website/src/routes/policies/+page.svelte | 71 - website/static/android-chrome-192x192.png | Bin 14056 -> 0 bytes website/static/android-chrome-512x512.png | Bin 10121 -> 0 bytes website/static/apple-touch-icon.png | Bin 11465 -> 0 bytes website/static/browserconfig.xml | 9 - website/static/favicon-16x16.png | Bin 1040 -> 0 bytes website/static/favicon-32x32.png | Bin 2021 -> 0 bytes website/static/favicon.ico | Bin 15086 -> 0 bytes website/static/logo.png | Bin 14909 -> 0 bytes website/static/logo.svg | 7 - website/static/mstile-150x150.png | Bin 8427 -> 0 bytes website/static/safari-pinned-tab.svg | 39 - website/static/site.webmanifest | 19 - website/svelte.config.js | 13 - website/tailwind.config.js | 41 - website/tsconfig.json | 14 - website/vite.config.ts | 6 - 82 files changed, 3090 insertions(+), 11134 deletions(-) delete mode 100644 registry/.env.example delete mode 100644 registry/Cargo.toml delete mode 100644 registry/src/endpoints/mod.rs delete mode 100644 registry/src/endpoints/packages.rs delete mode 100644 registry/src/endpoints/search.rs delete mode 100644 registry/src/errors.rs delete mode 100644 registry/src/main.rs delete mode 100644 src/cli/api_token.rs delete mode 100644 src/cli/auth.rs create mode 100644 src/cli/auth/login.rs create mode 100644 src/cli/auth/logout.rs create mode 100644 src/cli/auth/mod.rs create mode 100644 src/cli/auth/whoami.rs delete mode 100644 src/cli/config.rs create mode 100644 src/cli/config/default_index.rs create mode 100644 src/cli/config/mod.rs delete mode 100644 src/cli/root.rs delete mode 100644 src/dependencies/git.rs delete mode 100644 src/dependencies/mod.rs delete mode 100644 src/dependencies/registry.rs delete mode 100644 src/dependencies/resolution.rs delete mode 100644 src/dependencies/wally.rs delete mode 100644 src/index.rs delete mode 100644 src/linking_file.rs create mode 100644 src/lockfile.rs delete mode 100644 src/multithread.rs create mode 100644 src/names.rs delete mode 100644 src/package_name.rs delete mode 100644 src/patches.rs delete mode 100644 src/project.rs create mode 100644 src/source/mod.rs create mode 100644 src/source/pesde.rs delete mode 100644 tests/prelude.rs delete mode 100644 tests/resolver.rs delete mode 100644 website/.eslintignore delete mode 100644 website/.eslintrc.cjs delete mode 100644 website/.gitignore delete mode 100644 website/.npmrc delete mode 100644 website/.prettierignore delete mode 100644 website/.prettierrc delete mode 100644 website/bun.lockb delete mode 100644 website/package.json delete mode 100644 website/patches/simple-svelte-autocomplete@2.5.2.patch delete mode 100644 website/patches/tar-stream@3.1.7.patch delete mode 100644 website/postcss.config.js delete mode 100644 website/src/app.css delete mode 100644 website/src/app.d.ts delete mode 100644 website/src/app.html delete mode 100644 website/src/autocomplete.d.ts delete mode 100644 website/src/lib/Codeblock.svelte delete mode 100644 website/src/lib/markdown.ts delete mode 100644 website/src/routes/+layout.svelte delete mode 100644 website/src/routes/+page.svelte delete mode 100644 website/src/routes/+page.ts delete mode 100644 website/src/routes/docs/+page.svelte delete mode 100644 website/src/routes/docs/Note.svelte delete mode 100644 website/src/routes/packages/[scope]/[name]/[version]/+page.svelte delete mode 100644 website/src/routes/packages/[scope]/[name]/[version]/+page.ts delete mode 100644 website/src/routes/policies/+page.svelte delete mode 100644 website/static/android-chrome-192x192.png delete mode 100644 website/static/android-chrome-512x512.png delete mode 100644 website/static/apple-touch-icon.png delete mode 100644 website/static/browserconfig.xml delete mode 100644 website/static/favicon-16x16.png delete mode 100644 website/static/favicon-32x32.png delete mode 100644 website/static/favicon.ico delete mode 100644 website/static/logo.png delete mode 100644 website/static/logo.svg delete mode 100644 website/static/mstile-150x150.png delete mode 100644 website/static/safari-pinned-tab.svg delete mode 100644 website/static/site.webmanifest delete mode 100644 website/svelte.config.js delete mode 100644 website/tailwind.config.js delete mode 100644 website/tsconfig.json delete mode 100644 website/vite.config.ts diff --git a/Cargo.lock b/Cargo.lock index 4f92b83..2a80d03 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,270 +2,11 @@ # It is not intended for manual editing. version = 3 -[[package]] -name = "actix-codec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" -dependencies = [ - "bitflags 2.5.0", - "bytes", - "futures-core", - "futures-sink", - "memchr", - "pin-project-lite", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "actix-cors" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9e772b3bcafe335042b5db010ab7c09013dad6eac4915c91d8d50902769f331" -dependencies = [ - "actix-utils", - "actix-web", - "derive_more", - "futures-util", - "log", - "once_cell", - "smallvec", -] - -[[package]] -name = "actix-governor" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2e7b88f3804e01bd4191fdb08650430bbfcb43d3d9b2890064df3551ec7d25b" -dependencies = [ - "actix-http", - "actix-web", - "futures", - "governor", -] - -[[package]] -name = "actix-http" -version = "3.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d223b13fd481fc0d1f83bb12659ae774d9e3601814c68a0bc539731698cca743" -dependencies = [ - "actix-codec", - "actix-rt", - "actix-service", - "actix-utils", - "ahash", - "base64 0.21.7", - "bitflags 2.5.0", - "brotli 3.5.0", - "bytes", - "bytestring", - "derive_more", - "encoding_rs", - "flate2", - "futures-core", - "h2 0.3.26", - "http 0.2.12", - "httparse", - "httpdate", - "itoa", - "language-tags", - "local-channel", - "mime", - "percent-encoding", - "pin-project-lite", - "rand", - "sha1 0.10.6", - "smallvec", - "tokio", - "tokio-util", - "tracing", - "zstd 0.13.0", -] - -[[package]] -name = "actix-macros" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" -dependencies = [ - "quote", - "syn 2.0.63", -] - -[[package]] -name = "actix-multipart" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b960e2aea75f49c8f069108063d12a48d329fc8b60b786dfc7552a9d5918d2d" -dependencies = [ - "actix-multipart-derive", - "actix-utils", - "actix-web", - "bytes", - "derive_more", - "futures-core", - "futures-util", - "httparse", - "local-waker", - "log", - "memchr", - "mime", - "serde", - "serde_json", - "serde_plain", - "tempfile", - "tokio", -] - -[[package]] -name = "actix-multipart-derive" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a0a77f836d869f700e5b47ac7c3c8b9c8bc82e4aec861954c6198abee3ebd4d" -dependencies = [ - "darling", - "parse-size", - "proc-macro2", - "quote", - "syn 2.0.63", -] - -[[package]] -name = "actix-router" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d22475596539443685426b6bdadb926ad0ecaefdfc5fb05e5e3441f15463c511" -dependencies = [ - "bytestring", - "http 0.2.12", - "regex", - "serde", - "tracing", -] - -[[package]] -name = "actix-rt" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28f32d40287d3f402ae0028a9d54bef51af15c8769492826a69d28f81893151d" -dependencies = [ - "futures-core", - "tokio", -] - -[[package]] -name = "actix-server" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb13e7eef0423ea6eab0e59f6c72e7cb46d33691ad56a726b3cd07ddec2c2d4" -dependencies = [ - "actix-rt", - "actix-service", - "actix-utils", - "futures-core", - "futures-util", - "mio", - "socket2 0.5.7", - "tokio", - "tracing", -] - -[[package]] -name = "actix-service" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b894941f818cfdc7ccc4b9e60fa7e53b5042a2e8567270f9147d5591893373a" -dependencies = [ - "futures-core", - "paste", - "pin-project-lite", -] - -[[package]] -name = "actix-utils" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88a1dcdff1466e3c2488e1cb5c36a71822750ad43839937f85d2f4d9f8b705d8" -dependencies = [ - "local-waker", - "pin-project-lite", -] - -[[package]] -name = "actix-web" -version = "4.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43a6556ddebb638c2358714d853257ed226ece6023ef9364f23f0c70737ea984" -dependencies = [ - "actix-codec", - "actix-http", - "actix-macros", - "actix-router", - "actix-rt", - "actix-server", - "actix-service", - "actix-utils", - "actix-web-codegen", - "ahash", - "bytes", - "bytestring", - "cfg-if", - "cookie 0.16.2", - "derive_more", - "encoding_rs", - "futures-core", - "futures-util", - "itoa", - "language-tags", - "log", - "mime", - "once_cell", - "pin-project-lite", - "regex", - "serde", - "serde_json", - "serde_urlencoded", - "smallvec", - "socket2 0.5.7", - "time 0.3.36", - "url", -] - -[[package]] -name = "actix-web-codegen" -version = "4.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb1f50ebbb30eca122b188319a4398b3f7bb4a8cdf50ecfb73bfc6a3c3ce54f5" -dependencies = [ - "actix-router", - "proc-macro2", - "quote", - "syn 2.0.63", -] - -[[package]] -name = "actix-web-httpauth" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d613edf08a42ccc6864c941d30fe14e1b676a77d16f1dbadc1174d065a0a775" -dependencies = [ - "actix-utils", - "actix-web", - "base64 0.21.7", - "futures-core", - "futures-util", - "log", - "pin-project-lite", -] - [[package]] name = "addr2line" -version = "0.21.0" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "6e4503c46a5c0c7844e948c9a4d6acd9f50cccb4de1c48eb9e291ea17470c678" dependencies = [ "gimli", ] @@ -276,18 +17,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "aes" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e8b47f52ea9bae42228d07ec09eb676433d7c4ed1ebdf0f1d1c29ed446f1ab8" -dependencies = [ - "cfg-if", - "cipher 0.3.0", - "cpufeatures", - "opaque-debug", -] - [[package]] name = "aes" version = "0.8.4" @@ -295,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", - "cipher 0.4.4", + "cipher", "cpufeatures", ] @@ -306,7 +35,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -321,21 +49,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "alloc-no-stdlib" -version = "2.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" - -[[package]] -name = "alloc-stdlib" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" -dependencies = [ - "alloc-no-stdlib", -] - [[package]] name = "allocator-api2" version = "0.2.18" @@ -389,9 +102,9 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +checksum = "ad186efb764318d35165f1758e7dcef3b10628e26d41a44bc5550652e6804391" dependencies = [ "windows-sys 0.52.0", ] @@ -408,9 +121,18 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.83" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25bdb32cbbdce2b519a9cd7df3a678443100e265d5e25ca763b7572a5104f5f3" +checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" + +[[package]] +name = "arbitrary" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5a26814d8dcb93b0e5a0ff3c6d80a8843bafb21b39e8e18a6f05471870e110" +dependencies = [ + "derive_arbitrary", +] [[package]] name = "arc-swap" @@ -418,120 +140,44 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" -[[package]] -name = "arrayref" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" - -[[package]] -name = "arrayvec" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" - -[[package]] -name = "arrayvec" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" - [[package]] name = "async-broadcast" -version = "0.5.1" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c48ccdbf6ca6b121e0f586cbc0e73ae440e56c67c30fa0873b4e110d9c26d2b" +checksum = "20cd0e2e25ea8e5f7e9df04578dc6cf5c83577fd09b1a46aaf5c85e1c33f2a7e" dependencies = [ - "event-listener 2.5.3", + "event-listener", + "event-listener-strategy", "futures-core", + "pin-project-lite", ] [[package]] name = "async-channel" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.2", + "event-listener-strategy", "futures-core", "pin-project-lite", ] -[[package]] -name = "async-compression" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c90a406b4495d129f00461241616194cb8a032c8d1c53c657f0961d5f8e0498" -dependencies = [ - "brotli 6.0.0", - "flate2", - "futures-core", - "memchr", - "pin-project-lite", - "tokio", -] - -[[package]] -name = "async-executor" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b10202063978b3351199d68f8b22c4e47e4b1b822f8d43fd862d5ea8c006b29a" -dependencies = [ - "async-task", - "concurrent-queue", - "fastrand 2.1.0", - "futures-lite 2.3.0", - "slab", -] - -[[package]] -name = "async-fs" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279cf904654eeebfa37ac9bb1598880884924aab82e290aa65c9e77a0e142e06" -dependencies = [ - "async-lock 2.8.0", - "autocfg", - "blocking", - "futures-lite 1.13.0", -] - [[package]] name = "async-io" -version = "1.13.0" +version = "2.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc5b45d93ef0529756f812ca52e44c221b35341892d3dcc34132ac02f3dd2af" +checksum = "0d6baa8f0178795da0e71bc42c9e5d13261aac7ee549853162e66a241ba17964" dependencies = [ - "async-lock 2.8.0", - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-lite 1.13.0", - "log", - "parking", - "polling 2.8.0", - "rustix 0.37.27", - "slab", - "socket2 0.4.10", - "waker-fn", -] - -[[package]] -name = "async-io" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" -dependencies = [ - "async-lock 3.3.0", + "async-lock", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "parking", - "polling 3.7.0", - "rustix 0.38.34", + "polling", + "rustix", "slab", "tracing", "windows-sys 0.52.0", @@ -539,39 +185,33 @@ dependencies = [ [[package]] name = "async-lock" -version = "2.8.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "287272293e9d8c41773cec55e365490fe034813a2f172f502d6ddcf75b2f582b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 2.5.3", -] - -[[package]] -name = "async-lock" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" -dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener", + "event-listener-strategy", "pin-project-lite", ] [[package]] name = "async-process" -version = "1.8.1" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea6438ba0a08d81529c69b36700fa2f95837bfe3e776ab39cde9c14d9149da88" +checksum = "f7eda79bbd84e29c2b308d1dc099d7de8dcc7035e48f4bf5dc4a531a44ff5e2a" dependencies = [ - "async-io 1.13.0", - "async-lock 2.8.0", + "async-channel", + "async-io", + "async-lock", "async-signal", + "async-task", "blocking", "cfg-if", - "event-listener 3.1.0", - "futures-lite 1.13.0", - "rustix 0.38.34", - "windows-sys 0.48.0", + "event-listener", + "futures-lite", + "rustix", + "tracing", + "windows-sys 0.52.0", ] [[package]] @@ -582,22 +222,22 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "async-signal" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afe66191c335039c7bb78f99dc7520b0cbb166b3a1cb33a03f53d8a1c6f2afda" +checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" dependencies = [ - "async-io 2.3.2", - "async-lock 3.3.0", + "async-io", + "async-lock", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.34", + "rustix", "signal-hook-registry", "slab", "windows-sys 0.52.0", @@ -611,13 +251,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -626,17 +266,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" -[[package]] -name = "auth-git2" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51bd0e4592409df8631ca807716dc1e5caafae5d01ce0157c966c71c7e49c3c" -dependencies = [ - "dirs 5.0.1", - "git2", - "terminal-prompt", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -645,9 +274,9 @@ checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "5cc23269a4f8976d0a4d2e7109211a419fe30e8d88d677cd60b6bc79c5732e0a" dependencies = [ "addr2line", "cc", @@ -658,42 +287,12 @@ dependencies = [ "rustc-demangle", ] -[[package]] -name = "base-x" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbbc9d0964165b47557570cce6c952866c2678457aca742aafc9fb771d30270" - -[[package]] -name = "base64" -version = "0.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" - -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64ct" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" - -[[package]] -name = "beef" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" - [[package]] name = "bitflags" version = "1.3.2" @@ -702,41 +301,11 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" - -[[package]] -name = "bitpacking" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8c7d2ac73c167c06af4a5f37e6e59d84148d57ccbe4480b76f0273eefea82d7" +checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" dependencies = [ - "crunchy", -] - -[[package]] -name = "blake2b_simd" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" -dependencies = [ - "arrayref", - "arrayvec 0.5.2", - "constant_time_eq 0.1.5", -] - -[[package]] -name = "blake3" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" -dependencies = [ - "arrayref", - "arrayvec 0.7.4", - "cc", - "cfg-if", - "constant_time_eq 0.3.0", + "serde", ] [[package]] @@ -748,78 +317,28 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-modes" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cb03d1bed155d89dce0f845b7899b18a9a163e148fd004e1c28421a783e2d8e" -dependencies = [ - "block-padding", - "cipher 0.3.0", -] - [[package]] name = "block-padding" -version = "0.2.1" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d696c370c750c948ada61c69a0ee2cbbb9c50b1019ddb86d9317157a99c2cae" +checksum = "a8894febbff9f758034a5b8e12d87918f56dfc64a8e1fe757d65e29041538d93" +dependencies = [ + "generic-array", +] [[package]] name = "blocking" -version = "1.6.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "495f7104e962b7356f0aeb34247aca1fe7d2e783b346582db7f2904cb5717e88" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock 3.3.0", "async-task", "futures-io", - "futures-lite 2.3.0", + "futures-lite", "piper", ] -[[package]] -name = "brotli" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d640d25bc63c50fb1f0b545ffd80207d2e10a4c965530809b40ba3386825c391" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor 2.5.1", -] - -[[package]] -name = "brotli" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", - "brotli-decompressor 4.0.0", -] - -[[package]] -name = "brotli-decompressor" -version = "2.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e2e4afe60d7dd600fdd3de8d0f08c2b7ec039712e3b6137ff98b7004e82de4f" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - -[[package]] -name = "brotli-decompressor" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6221fe77a248b9117d431ad93761222e1cf8ff282d9d1d5d9f53d6299a1cf76" -dependencies = [ - "alloc-no-stdlib", - "alloc-stdlib", -] - [[package]] name = "bstr" version = "1.9.1" @@ -827,6 +346,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" dependencies = [ "memchr", + "regex-automata", "serde", ] @@ -842,12 +362,6 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ce89b21cab1437276d2650d57e971f9d548a2d9037cc231abdc0562b97498ce" -[[package]] -name = "byteorder" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fc10e8cc6b2580fda3f36eb6dc5316657f812a3df879a44a66fc9f0fdbc4855" - [[package]] name = "byteorder" version = "1.5.0" @@ -860,15 +374,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" -[[package]] -name = "bytestring" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d80203ea6b29df88012294f62733de21cfeab47f17b41af3a38bc30a03ee72" -dependencies = [ - "bytes", -] - [[package]] name = "bzip2" version = "0.4.4" @@ -891,22 +396,25 @@ dependencies = [ ] [[package]] -name = "cc" -version = "1.0.97" +name = "cbc" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "099a5357d84c4c61eb35fc8eafa9a79a902c2f76911e5747ced4e032edd8d9b4" +checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6" +dependencies = [ + "cipher", +] + +[[package]] +name = "cc" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "907d8581360765417f8f2e0e7d602733bbed60156b4465b7617243689ef9b83d" dependencies = [ "jobserver", "libc", "once_cell", ] -[[package]] -name = "census" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f4c707c6a209cbe82d10abd08e1ea8995e9ea937d2550646e02798948992be0" - [[package]] name = "cfg-if" version = "1.0.0" @@ -915,15 +423,15 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.34" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bc015644b92d5890fab7489e49d21f879d5c990186827d42ec511919404f38b" +checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" dependencies = [ "android-tzdata", "iana-time-zone", @@ -931,31 +439,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.5", -] - -[[package]] -name = "chrono_lc" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809c968b91b4949802fba00279fd0610158cbb2ae5f10663f0c3d5331f88d6d6" -dependencies = [ - "chrono", - "lazy_static", - "num-integer", - "serde", - "serde_derive", - "serde_json", - "walkdir", -] - -[[package]] -name = "cipher" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7" -dependencies = [ - "generic-array", + "windows-targets 0.52.6", ] [[package]] @@ -970,9 +454,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.4" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "64acc1846d54c1fe936a78dc189c34e28d3f5afc348403f28ecf53660b9b8462" dependencies = [ "clap_builder", "clap_derive", @@ -980,42 +464,39 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "6fb8393d67ba2e7bfaf28a23458e4e2b543cc73a99595511eb207fdb8aede942" dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim 0.11.1", + "strsim", ] [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2bac35c6dafb060fd4d275d9a4ffae97917c13a6327903a8be2153cd964f7085" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "4b82cf0babdbd58558212896d1a4272303a57bdb245c2bf1147185fb45640e70" [[package]] -name = "clipboard-win" -version = "5.3.1" +name = "clru" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" -dependencies = [ - "error-code", -] +checksum = "cbd0f76e066e64fdc5631e3bb46381254deab9ef1158292f27c8c57e3bf3fe59" [[package]] name = "colorchoice" @@ -1023,6 +504,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" +[[package]] +name = "colored" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbf2150cce219b664a8a70df7a1f933836724b503f8a413af9365b4dcc4d90b8" +dependencies = [ + "lazy_static", + "windows-sys 0.48.0", +] + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1045,18 +536,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "const_fn" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373e9fafaa20882876db20562275ff58d50e0caa2590077fe7ce7bef90211d0d" - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - [[package]] name = "constant_time_eq" version = "0.3.0" @@ -1069,27 +548,6 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" -[[package]] -name = "cookie" -version = "0.15.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6e25dfc584d06a3dbf775d207ff00d7de98d824c952dd2233dfbb261889a42" -dependencies = [ - "time 0.2.27", - "version_check", -] - -[[package]] -name = "cookie" -version = "0.16.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb" -dependencies = [ - "percent-encoding", - "time 0.3.36", - "version_check", -] - [[package]] name = "core-foundation" version = "0.9.4" @@ -1116,21 +574,27 @@ dependencies = [ ] [[package]] -name = "crc32fast" -version = "1.4.0" +name = "crc" +version = "3.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" dependencies = [ - "cfg-if", + "crc-catalog", ] [[package]] -name = "crossbeam-channel" -version = "0.5.12" +name = "crc-catalog" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ - "crossbeam-utils", + "cfg-if", ] [[package]] @@ -1154,9 +618,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" [[package]] name = "crossterm" @@ -1183,12 +647,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - [[package]] name = "crypto-common" version = "0.1.6" @@ -1201,9 +659,9 @@ dependencies = [ [[package]] name = "darling" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" dependencies = [ "darling_core", "darling_macro", @@ -1211,57 +669,63 @@ dependencies = [ [[package]] name = "darling_core" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" dependencies = [ "fnv", "ident_case", "proc-macro2", "quote", - "strsim 0.10.0", - "syn 2.0.63", + "strsim", + "syn 2.0.71", ] [[package]] name = "darling_macro" -version = "0.20.8" +version = "0.20.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] -name = "dashmap" -version = "5.5.3" +name = "dbus" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +checksum = "1bb21987b9fb1613058ba3843121dd18b163b254d8a6e797e144cbac14d96d1b" dependencies = [ - "cfg-if", - "hashbrown 0.14.5", - "lock_api", + "libc", + "libdbus-sys", + "winapi", +] + +[[package]] +name = "dbus-secret-service" +version = "4.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba3ae92ba157c1fd7e3453bf8fad9cd2e62783fe5f65047fd3b52fcbf6594aef" +dependencies = [ + "aes", + "block-padding", + "cbc", + "dbus", + "futures-util", + "hkdf", + "num", "once_cell", - "parking_lot_core", + "rand", + "sha2", ] [[package]] -name = "data-encoding" -version = "2.6.0" +name = "deflate64" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" - -[[package]] -name = "debugid" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef552e6f588e446098f6ba40d89ac146c8c7b64aade83c051ee00bb5d2bc18d" -dependencies = [ - "serde", - "uuid", -] +checksum = "83ace6c86376be0b6cdcf3fb41882e81d94b31587573d1cfa9d01cd06bba210d" [[package]] name = "deranged" @@ -1274,40 +738,27 @@ dependencies = [ ] [[package]] -name = "derivative" -version = "2.2.0" +name = "derive_arbitrary" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc3dd5e9e9c0b295d6e1e4d811fb6f157d5ffd784b8d202fc62eac8035a770b" +checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.71", ] [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" dependencies = [ "convert_case", "proc-macro2", "quote", - "rustc_version 0.4.0", - "syn 1.0.109", -] - -[[package]] -name = "dialoguer" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bce805d770f407bc62102fca7c2c64ceef2fbcb2b8bd19d2765ce093980de" -dependencies = [ - "console", - "shell-words", - "tempfile", - "thiserror", - "zeroize", + "rustc_version", + "syn 2.0.71", ] [[package]] @@ -1330,26 +781,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" -dependencies = [ - "libc", - "redox_users 0.3.5", - "winapi", -] - -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys", -] - [[package]] name = "dirs-sys" version = "0.4.1" @@ -1358,27 +789,20 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users 0.4.5", + "redox_users", "windows-sys 0.48.0", ] [[package]] -name = "discard" -version = "1.0.4" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" - -[[package]] -name = "dotenvy" -version = "0.15.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" - -[[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] [[package]] name = "dunce" @@ -1392,12 +816,6 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" -[[package]] -name = "either" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" - [[package]] name = "encode_unicode" version = "0.3.6" @@ -1414,16 +832,16 @@ dependencies = [ ] [[package]] -name = "endian-type" -version = "0.1.2" +name = "endi" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" [[package]] name = "enumflags2" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +checksum = "d232db7f5956f3f14313dc2f87985c58bd2c695ce124c8cdd984e08e15ac133d" dependencies = [ "enumflags2_derive", "serde", @@ -1431,23 +849,13 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", -] - -[[package]] -name = "env_filter" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a009aa4810eb158359dda09d0c87378e4bbb89b5a801f016885a4707ba24f7ea" -dependencies = [ - "log", - "regex", + "syn 2.0.71", ] [[package]] @@ -1463,34 +871,12 @@ dependencies = [ "termcolor", ] -[[package]] -name = "env_logger" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38b35839ba51819680ba087cd351788c9a3c476841207e0b8cee0b04722343b9" -dependencies = [ - "anstream", - "anstyle", - "env_filter", - "humantime", - "log", -] - [[package]] name = "equivalent" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -[[package]] -name = "erased-serde" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3" -dependencies = [ - "serde", -] - [[package]] name = "errno" version = "0.3.9" @@ -1501,85 +887,32 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "error-code" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" - [[package]] name = "event-listener" -version = "2.5.3" +version = "5.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0206175f82b8d6bf6652ff7d71a1e27fd2e4efde587fd368662814d6ec1d9ce0" - -[[package]] -name = "event-listener" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d93877bcde0eb80ca09131a08d23f0a5c18a620b01db137dba666d18cd9b30c2" +checksum = "6032be9bd27023a771701cc49f9f053c751055f71efb2e0ae5c15809093675ba" dependencies = [ "concurrent-queue", "parking", "pin-project-lite", ] -[[package]] -name = "event-listener" -version = "4.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" -dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - [[package]] name = "event-listener-strategy" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f214dc438f977e6d4e3500aaa277f5ad94ca83fbbd9b1a15713ce2344ccc5a1" dependencies = [ - "event-listener 5.3.0", + "event-listener", "pin-project-lite", ] [[package]] -name = "fastdivide" -version = "0.4.1" +name = "faster-hex" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59668941c55e5c186b8b58c391629af56774ec768f73c08bbcd56f09348eb00b" - -[[package]] -name = "fastrand" -version = "1.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "a2a2b11eda1d40935b26cf18f6833c526845ae8c41e58d09af6adeb6f0269183" [[package]] name = "fastrand" @@ -1587,17 +920,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9fc0510504f03c51ada170672ac806f1f105a88aa97a5281117e1ddc3368e51a" -[[package]] -name = "fd-lock" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e5768da2206272c81ef0b5e951a41862938a6070da63bcea197899942d3b947" -dependencies = [ - "cfg-if", - "rustix 0.38.34", - "windows-sys 0.52.0", -] - [[package]] name = "filetime" version = "0.2.23" @@ -1610,18 +932,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "findshlibs" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b9e59cd0f7e0806cca4be089683ecb6434e602038df21fe6bf6711b2f07f64" -dependencies = [ - "cc", - "lazy_static", - "libc", - "winapi", -] - [[package]] name = "flate2" version = "1.0.30" @@ -1638,21 +948,6 @@ 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" @@ -1662,31 +957,19 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "fs4" -version = "0.6.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eeb4ed9e12f43b7fa0baae3f9cdda28352770132ef2e09a23760c29cae8bd47" -dependencies = [ - "rustix 0.38.34", - "windows-sys 0.48.0", -] - [[package]] name = "full_moon" -version = "0.19.0" +version = "1.0.0-rc.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ef4f8ad0689d3a86bb483650422d72e6f79a37fdc83ed5426cafe96b776ce1" +checksum = "1c23cbff7830f7a5370221b5c073bafc74b5d9e8555873b1f153a41416837e4e" dependencies = [ "bytecount", "cfg-if", "derive_more", "full_moon_derive", - "logos", "paste", "serde", "smol_str", - "stacker", ] [[package]] @@ -1701,21 +984,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.30" @@ -1732,45 +1000,19 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" -[[package]] -name = "futures-executor" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" -[[package]] -name = "futures-lite" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" -dependencies = [ - "fastrand 1.9.0", - "futures-core", - "futures-io", - "memchr", - "parking", - "pin-project-lite", - "waker-fn", -] - [[package]] name = "futures-lite" version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" dependencies = [ - "fastrand 2.1.0", + "fastrand", "futures-core", "futures-io", "parking", @@ -1785,7 +1027,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -1800,19 +1042,12 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" -[[package]] -name = "futures-timer" -version = "3.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24" - [[package]] name = "futures-util" version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -1839,20 +1074,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" dependencies = [ - "byteorder 1.5.0", -] - -[[package]] -name = "generator" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e" -dependencies = [ - "cc", - "libc", - "log", - "rustversion", - "windows 0.48.0", + "byteorder", ] [[package]] @@ -1865,17 +1087,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "getrandom" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.9.0+wasi-snapshot-preview1", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -1884,41 +1095,787 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", ] [[package]] name = "gimli" -version = "0.28.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "40ecd4077b5ae9fd2e9e169b102c6c330d0605168eb0e8bf79952b256dbefffd" [[package]] -name = "git2" -version = "0.18.3" +name = "gix" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232e6a7bfe35766bf715e55a88b39a700596c0ccfd88cd3680b4cdb40d66ef70" +checksum = "984c5018adfa7a4536ade67990b3ebc6e11ab57b3d6cd9968de0947ca99b4b06" dependencies = [ - "bitflags 2.5.0", + "gix-actor", + "gix-attributes", + "gix-command", + "gix-commitgraph", + "gix-config", + "gix-credentials", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-filter", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-ignore", + "gix-index", + "gix-lock", + "gix-macros", + "gix-mailmap", + "gix-negotiate", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-pathspec", + "gix-prompt", + "gix-protocol", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-submodule", + "gix-tempfile", + "gix-trace", + "gix-transport", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "gix-worktree", + "once_cell", + "parking_lot", + "regex", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-actor" +version = "0.31.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b8ee65074b2bbb91d9d97c15d172ea75043aefebf9869b5b329149dc76501c" +dependencies = [ + "bstr", + "gix-date", + "gix-utils", + "itoa", + "serde", + "thiserror", + "winnow 0.6.13", +] + +[[package]] +name = "gix-attributes" +version = "0.22.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eefb48f42eac136a4a0023f49a54ec31be1c7a9589ed762c45dcb9b953f7ecc8" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-quote", + "gix-trace", + "kstring", + "serde", + "smallvec", + "thiserror", + "unicode-bom", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a371db66cbd4e13f0ed9dc4c0fea712d7276805fccc877f77e96374d317e87ae" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-chunk" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45c8751169961ba7640b513c3b24af61aa962c967aaf04116734975cd5af0c52" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-command" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c22e086314095c43ffe5cdc5c0922d5439da4fd726f3b0438c56147c34dc225" +dependencies = [ + "bstr", + "gix-path", + "gix-trace", + "shell-words", +] + +[[package]] +name = "gix-commitgraph" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7b102311085da4af18823413b5176d7c500fb2272eaf391cfa8635d8bcb12c4" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2", + "serde", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.37.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53fafe42957e11d98e354a66b6bd70aeea00faf2f62dd11164188224a507c840" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", + "winnow 0.6.13", +] + +[[package]] +name = "gix-config-value" +version = "0.14.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbd06203b1a9b33a78c88252a625031b094d9e1b647260070c25b09910c0a804" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "gix-path", "libc", - "libgit2-sys", - "log", - "openssl-probe", - "openssl-sys", + "thiserror", +] + +[[package]] +name = "gix-credentials" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c70146183bd3c7119329a3c7392d1aa0e0adbe48d727f4df31828fe6d8fdaa1" +dependencies = [ + "bstr", + "gix-command", + "gix-config-value", + "gix-path", + "gix-prompt", + "gix-sec", + "gix-trace", + "gix-url", + "serde", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9eed6931f21491ee0aeb922751bd7ec97b4b2fe8fbfedcb678e2a2dce5f3b8c0" +dependencies = [ + "bstr", + "itoa", + "serde", + "thiserror", + "time", +] + +[[package]] +name = "gix-diff" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40b9bd8b2d07b6675a840b56a6c177d322d45fa082672b0dad8f063b25baf0a4" +dependencies = [ + "bstr", + "gix-hash", + "gix-object", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc27c699b63da66b50d50c00668bc0b7e90c3a382ef302865e891559935f3dbf" +dependencies = [ + "bstr", + "dunce", + "gix-fs", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror", +] + +[[package]] +name = "gix-features" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac7045ac9fe5f9c727f38799d002a7ed3583cd777e3322a7c4b43e3cf437dc69" +dependencies = [ + "bytes", + "crc32fast", + "flate2", + "gix-hash", + "gix-trace", + "gix-utils", + "libc", + "once_cell", + "prodash", + "sha1_smol", + "thiserror", + "walkdir", +] + +[[package]] +name = "gix-filter" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00ce6ea5ac8fca7adbc63c48a1b9e0492c222c386aa15f513405f1003f2f4ab2" +dependencies = [ + "bstr", + "encoding_rs", + "gix-attributes", + "gix-command", + "gix-hash", + "gix-object", + "gix-packetline-blocking", + "gix-path", + "gix-quote", + "gix-trace", + "gix-utils", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-fs" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3338ff92a2164f5209f185ec0cd316f571a72676bb01d27e22f2867ba69f77a" +dependencies = [ + "fastrand", + "gix-features", + "gix-utils", +] + +[[package]] +name = "gix-glob" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a29ad0990cf02c48a7aac76ed0dbddeb5a0d070034b83675cc3bbf937eace4" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "gix-features", + "gix-path", + "serde", +] + +[[package]] +name = "gix-hash" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f93d7df7366121b5018f947a04d37f034717e113dcf9ccd85c34b58e57a74d5e" +dependencies = [ + "faster-hex", + "serde", + "thiserror", +] + +[[package]] +name = "gix-hashtable" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ddf80e16f3c19ac06ce415a38b8591993d3f73aede049cb561becb5b3a8e242" +dependencies = [ + "gix-hash", + "hashbrown 0.14.5", + "parking_lot", +] + +[[package]] +name = "gix-ignore" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "640dbeb4f5829f9fc14d31f654a34a0350e43a24e32d551ad130d99bf01f63f1" +dependencies = [ + "bstr", + "gix-glob", + "gix-path", + "gix-trace", + "serde", + "unicode-bom", +] + +[[package]] +name = "gix-index" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d8c5a5f1c58edcbc5692b174cda2703aba82ed17d7176ff4c1752eb48b1b167" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "filetime", + "fnv", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "gix-utils", + "gix-validate", + "hashbrown 0.14.5", + "itoa", + "libc", + "memmap2", + "rustix", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bc7fe297f1f4614774989c00ec8b1add59571dc9b024b4c00acb7dedd4e19d" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-macros" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "999ce923619f88194171a67fb3e6d613653b8d4d6078b529b15a765da0edcc17" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] + +[[package]] +name = "gix-mailmap" +version = "0.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cb2da346958252cbc8656529f5479830a3bc6046f3d86405c9e77f71dfdf7b2" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "serde", + "thiserror", +] + +[[package]] +name = "gix-negotiate" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d57dec54544d155a495e01de947da024471e1825d7d3f2724301c07a310d6184" +dependencies = [ + "bitflags 2.6.0", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-object" +version = "0.42.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25da2f46b4e7c2fa7b413ce4dffb87f69eaf89c2057e386491f4c55cadbfe386" +dependencies = [ + "bstr", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-utils", + "gix-validate", + "itoa", + "serde", + "smallvec", + "thiserror", + "winnow 0.6.13", +] + +[[package]] +name = "gix-odb" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e92b9790e2c919166865d0825b26cc440a387c175bed1b43a2fa99c0e9d45e98" +dependencies = [ + "arc-swap", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "serde", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-pack" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a8da51212dbff944713edb2141ed7e002eea326b8992070374ce13a6cb610b3" +dependencies = [ + "clru", + "gix-chunk", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "memmap2", + "parking_lot", + "serde", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-packetline" +version = "0.17.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b70486beda0903b6d5b65dfa6e40585098cdf4e6365ca2dff4f74c387354a515" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror", +] + +[[package]] +name = "gix-packetline-blocking" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31d42378a3d284732e4d589979930d0d253360eccf7ec7a80332e5ccb77e14a" +dependencies = [ + "bstr", + "faster-hex", + "gix-trace", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca987128ffb056d732bd545db5db3d8b103d252fbf083c2567bb0796876619a4" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-pathspec" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76cab098dc10ba2d89f634f66bf196dea4d7db4bf10b75c7a9c201c55a2ee19" +dependencies = [ + "bitflags 2.6.0", + "bstr", + "gix-attributes", + "gix-config-value", + "gix-glob", + "gix-path", + "thiserror", +] + +[[package]] +name = "gix-prompt" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fddabbc7c51c241600ab3c4623b19fa53bde7c1a2f637f61043ed5fcadf000cc" +dependencies = [ + "gix-command", + "gix-config-value", + "parking_lot", + "rustix", + "thiserror", +] + +[[package]] +name = "gix-protocol" +version = "0.45.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c140d4c6d209048826bad78f021a01b612830f89da356efeb31afe8957f8bee" +dependencies = [ + "bstr", + "gix-credentials", + "gix-date", + "gix-features", + "gix-hash", + "gix-transport", + "gix-utils", + "maybe-async", + "serde", + "thiserror", + "winnow 0.6.13", +] + +[[package]] +name = "gix-quote" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbff4f9b9ea3fa7a25a70ee62f545143abef624ac6aa5884344e70c8b0a1d9ff" +dependencies = [ + "bstr", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.44.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3394a2997e5bc6b22ebc1e1a87b41eeefbcfcff3dbfa7c4bd73cb0ac8f1f3e2e" +dependencies = [ + "gix-actor", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-utils", + "gix-validate", + "memmap2", + "serde", + "thiserror", + "winnow 0.6.13", +] + +[[package]] +name = "gix-refspec" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde848865834a54fe4d9b4573f15d0e9a68eaf3d061b42d3ed52b4b8acf880b2" +dependencies = [ + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e08f8107ed1f93a83bcfbb4c38084c7cb3f6cd849793f1d5eec235f9b13b2b" +dependencies = [ + "bstr", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "gix-trace", + "serde", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4181db9cfcd6d1d0fd258e91569dbb61f94cb788b441b5294dd7f1167a3e788f" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fddc27984a643b20dd03e97790555804f98cf07404e0e552c0ad8133266a79a1" +dependencies = [ + "bitflags 2.6.0", + "gix-path", + "libc", + "serde", + "windows-sys 0.52.0", +] + +[[package]] +name = "gix-submodule" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "921cd49924ac14b6611b22e5fb7bbba74d8780dc7ad26153304b64d1272460ac" +dependencies = [ + "bstr", + "gix-config", + "gix-path", + "gix-pathspec", + "gix-refspec", + "gix-url", + "thiserror", +] + +[[package]] +name = "gix-tempfile" +version = "14.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b0e276cd08eb2a22e9f286a4f13a222a01be2defafa8621367515375644b99" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f924267408915fddcd558e3f37295cc7d6a3e50f8bd8b606cee0808c3915157e" + +[[package]] +name = "gix-transport" +version = "0.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb0ffa5f869977f5b9566399154055902f05d7e85c787d5eacf551acdd0c4adf" +dependencies = [ + "base64", + "bstr", + "gix-command", + "gix-credentials", + "gix-features", + "gix-packetline", + "gix-quote", + "gix-sec", + "gix-url", + "reqwest", + "serde", + "thiserror", +] + +[[package]] +name = "gix-traverse" +version = "0.39.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f20cb69b63eb3e4827939f42c05b7756e3488ef49c25c412a876691d568ee2a0" +dependencies = [ + "bitflags 2.6.0", + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-url" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0db829ebdca6180fbe32be7aed393591df6db4a72dbbc0b8369162390954d1cf" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "home", + "serde", + "thiserror", "url", ] [[package]] -name = "glam" -version = "0.27.0" +name = "gix-utils" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e05e7e6723e3455f4818c7b26e855439f7546cf617ef669d1adedb8669e5cb9" +checksum = "35192df7fd0fa112263bad8021e2df7167df4cc2a6e6d15892e1e55621d3d4dc" +dependencies = [ + "fastrand", + "unicode-normalization", +] [[package]] -name = "glob" -version = "0.3.1" +name = "gix-validate" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +checksum = "82c27dd34a49b1addf193c92070bcbf3beaf6e10f16a78544de6372e146a0acf" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "gix-worktree" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f6b7de83839274022aff92157d7505f23debf739d257984a300a35972ca94e" +dependencies = [ + "bstr", + "gix-attributes", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-ignore", + "gix-index", + "gix-object", + "gix-path", + "gix-validate", + "serde", +] [[package]] name = "globset" @@ -1929,61 +1886,22 @@ dependencies = [ "aho-corasick", "bstr", "log", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", -] - -[[package]] -name = "governor" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68a7f542ee6b35af73b06abc0dad1c1bae89964e4e253bc4b587b91c9637867b" -dependencies = [ - "cfg-if", - "dashmap", - "futures", - "futures-timer", - "no-std-compat", - "nonzero_ext", - "parking_lot", - "portable-atomic", - "quanta", - "rand", - "smallvec", - "spinning_top", + "regex-automata", + "regex-syntax", ] [[package]] name = "h2" -version = "0.3.26" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab" dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.2.6", - "slab", - "tokio", - "tokio-util", - "tracing", -] - -[[package]] -name = "h2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "816ec7294445779408f36fe57bc5b7fc1cf59664059096c65f905c1c61f58069" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 1.1.0", + "http", "indexmap 2.2.6", "slab", "tokio", @@ -2019,6 +1937,12 @@ version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + [[package]] name = "hex" version = "0.4.3" @@ -2052,34 +1976,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "hostname" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9c7c7c8ac16c798734b8a24560c1362120597c40d5e1459f09498f8f6c8f2ba" -dependencies = [ - "cfg-if", - "libc", - "windows 0.52.0", -] - -[[package]] -name = "htmlescape" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9025058dae765dee5070ec375f591e2ba14638c63feff74f13805a72e523163" - -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.1.0" @@ -2093,49 +1989,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 0.2.12", - "pin-project-lite", -] - -[[package]] -name = "http-body" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cac85db508abc24a2e48553ba12a996e87244a0395ce011e62b37158745d643" -dependencies = [ - "bytes", - "http 1.1.0", + "http", ] [[package]] name = "http-body-util" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0475f8b2ac86659c21b64320d5d653f9efe42acd2a4e560073ec61a155a34f1d" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", - "futures-core", - "http 1.1.0", - "http-body 1.0.0", + "futures-util", + "http", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -[[package]] -name = "httpdate" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +checksum = "0fcc0b4a115bf80b728eb8ea024ad5bd707b615bfed49e0665b6e0f86fd082d9" [[package]] name = "humantime" @@ -2145,42 +2024,17 @@ checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" [[package]] name = "hyper" -version = "0.14.28" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.5.7", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe575dd17d0862a9a33781c8c4696a55c320909004a67a00fb286ba8b1bc496d" +checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.4", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "httparse", - "httpdate", "itoa", "pin-project-lite", "smallvec", @@ -2190,80 +2044,36 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155" dependencies = [ "futures-util", - "http 0.2.12", - "hyper 0.14.28", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0bea761b46ae2b24eb4aef630d8d1c398157b6fc29e6350ecf090a0b70c952c" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.3.1", + "http", + "hyper", "hyper-util", - "rustls 0.22.4", + "rustls", "rustls-pki-types", "tokio", - "tokio-rustls 0.25.0", + "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 1.3.1", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", -] - -[[package]] -name = "hyper-tungstenite" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a343d17fe7885302ed7252767dc7bb83609a874b6ff581142241ec4b73957ad" -dependencies = [ - "http-body-util", - "hyper 1.3.1", - "hyper-util", - "pin-project-lite", - "tokio", - "tokio-tungstenite", - "tungstenite", + "webpki-roots", ] [[package]] name = "hyper-util" -version = "0.1.3" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca38ef113da30126bbff9cd1705f9273e15d45498615d138b0c20279ac7a76aa" +checksum = "3ab92f4f49ee4fb4f997c784b7a2e0fa70050211e0b6a287f898c3c9785ca956" dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.0", - "hyper 1.3.1", + "http", + "http-body", + "hyper", "pin-project-lite", - "socket2 0.5.7", + "socket2", "tokio", "tower", "tower-service", @@ -2319,32 +2129,12 @@ dependencies = [ "globset", "log", "memchr", - "regex-automata 0.4.6", + "regex-automata", "same-file", "walkdir", "winapi-util", ] -[[package]] -name = "include_dir" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" -dependencies = [ - "glob", - "include_dir_macros", -] - -[[package]] -name = "include_dir_macros" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" -dependencies = [ - "proc-macro2", - "quote", -] - [[package]] name = "indexmap" version = "1.9.3" @@ -2353,6 +2143,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -2363,6 +2154,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown 0.14.5", + "serde", ] [[package]] @@ -2394,6 +2186,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" dependencies = [ + "block-padding", "generic-array", ] @@ -2403,7 +2196,7 @@ version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fddf93031af70e75410a2511ec04d49e758ed2f26dad3404a934e0fb45cc12a" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -2416,25 +2209,11 @@ dependencies = [ [[package]] name = "instant" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi", - "libc", - "windows-sys 0.48.0", ] [[package]] @@ -2443,41 +2222,42 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.52.0", ] +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" -[[package]] -name = "itertools" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" -dependencies = [ - "either", -] - -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.11" @@ -2504,12 +2284,12 @@ dependencies = [ [[package]] name = "keyring" -version = "2.3.3" +version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "363387f0019d714aa60cc30ab4fe501a747f4c08fc58f069dd14be971bd495a0" +checksum = "f32930aef50aff920e88e6cf66b70a6d587a6f2bae6aeade5c1e583661a40f8e" dependencies = [ - "byteorder 1.5.0", - "lazy_static", + "byteorder", + "dbus-secret-service", "linux-keyutils", "secret-service", "security-framework", @@ -2517,51 +2297,98 @@ dependencies = [ ] [[package]] -name = "language-tags" -version = "0.3.2" +name = "kstring" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "levenshtein_automata" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" - -[[package]] -name = "libc" -version = "0.2.154" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae743338b92ff9146ce83992f766a31066a91a8c84a45e0e9f21e7cf6de6d346" - -[[package]] -name = "libgit2-sys" -version = "0.16.2+1.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4126d8b4ee5c9d9ea891dd875cfdc1e9d0950437179104b183d7d8a74d24e8" +checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747" dependencies = [ - "cc", - "libc", - "libssh2-sys", - "libz-sys", - "openssl-sys", - "pkg-config", + "serde", + "static_assertions", ] [[package]] -name = "libloading" -version = "0.8.3" +name = "lazy_static" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lexical-core" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" dependencies = [ - "cfg-if", - "windows-targets 0.52.5", + "lexical-parse-float", + "lexical-parse-integer", + "lexical-util", + "lexical-write-float", + "lexical-write-integer", +] + +[[package]] +name = "lexical-parse-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" +dependencies = [ + "lexical-parse-integer", + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-parse-integer" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "lexical-util" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "lexical-write-float" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" +dependencies = [ + "lexical-util", + "lexical-write-integer", + "static_assertions", +] + +[[package]] +name = "lexical-write-integer" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" +dependencies = [ + "lexical-util", + "static_assertions", +] + +[[package]] +name = "libc" +version = "0.2.155" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" + +[[package]] +name = "libdbus-sys" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06085512b750d640299b79be4bad3d2fa90a9c00b1fd9e1b46364f66f0485c72" +dependencies = [ + "pkg-config", ] [[package]] @@ -2570,80 +2397,25 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] -[[package]] -name = "libssh2-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee" -dependencies = [ - "cc", - "libc", - "libz-sys", - "openssl-sys", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "libz-sys" -version = "1.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - -[[package]] -name = "line-wrap" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd1bc4d24ad230d21fb898d1116b1801d7adfc449d42026475862ab48b11e70e" - [[package]] name = "linux-keyutils" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "761e49ec5fd8a5a463f9b84e877c373d888935b71c6be78f3767fe2ae6bed18e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "libc", ] [[package]] name = "linux-raw-sys" -version = "0.3.8" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -[[package]] -name = "linux-raw-sys" -version = "0.4.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" - -[[package]] -name = "local-channel" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6cbc85e69b8df4b8bb8b89ec634e7189099cea8927a276b7384ce5488e53ec8" -dependencies = [ - "futures-core", - "futures-sink", - "local-waker", -] - -[[package]] -name = "local-waker" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d873d7c67ce09b42110d801813efbc9364414e356be9935700d368351657487" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "lock_api" @@ -2656,205 +2428,53 @@ dependencies = [ ] [[package]] -name = "log" -version = "0.4.21" +name = "lockfree-object-pool" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" [[package]] -name = "logos" -version = "0.12.1" +name = "log" +version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8b031682c67a8e3d5446840f9573eb7fe26efe7ec8d195c9ac4c0647c502f1" +checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" dependencies = [ - "logos-derive", + "byteorder", + "crc", ] [[package]] -name = "logos-derive" -version = "0.12.1" +name = "maybe-async" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d849148dbaf9661a6151d1ca82b13bb4c4c128146a88d05253b38d4e2f496c" +checksum = "5cf92c10c7e361d6b99666ec1c6f9805b0bea2c3bd8c78dc6fe98ac5bd78db11" dependencies = [ - "beef", - "fnv", "proc-macro2", "quote", - "regex-syntax 0.6.29", - "syn 1.0.109", -] - -[[package]] -name = "loom" -version = "0.5.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5" -dependencies = [ - "cfg-if", - "generator", - "pin-utils", - "scoped-tls", - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "lru" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a83fb7698b3643a0e34f9ae6f2e8f0178c0fd42f8b59d493aa271ff3a5bf21" -dependencies = [ - "hashbrown 0.14.5", -] - -[[package]] -name = "luau0-src" -version = "0.8.6+luau622" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07758c1f5908f7f9dd9109efaf8c66907cc38acf312db03287e7ad2a64b5de1c" -dependencies = [ - "cc", -] - -[[package]] -name = "lune" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a98794d9d507af3f382df7ef137605aac2a4f88eded2c731ce9d59ffafdb4d51" -dependencies = [ - "anyhow", - "async-compression", - "async-trait", - "blocking", - "chrono", - "chrono_lc", - "clap", - "console", - "dialoguer", - "directories", - "dunce", - "env_logger 0.11.3", - "futures-util", - "glam", - "http 1.1.0", - "http-body-util", - "hyper 1.3.1", - "hyper-tungstenite", - "hyper-util", - "include_dir", - "itertools 0.12.1", - "lz4_flex", - "mlua", - "mlua-luau-scheduler", - "once_cell", - "os_str_bytes", - "path-clean", - "pathdiff", - "pin-project", - "rand", - "rbx_binary", - "rbx_cookie", - "rbx_dom_weak", - "rbx_reflection", - "rbx_reflection_database", - "rbx_xml", - "regex", - "reqwest 0.11.27", - "rustyline", - "serde", - "serde_json", - "serde_yaml", - "thiserror", - "tokio", - "tokio-tungstenite", - "toml", - "tracing", - "tracing-subscriber", - "urlencoding", -] - -[[package]] -name = "lz4" -version = "1.24.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e9e2dd86df36ce760a60f6ff6ad526f7ba1f14ba0356f8254fb6905e6494df1" -dependencies = [ - "libc", - "lz4-sys", -] - -[[package]] -name = "lz4-sys" -version = "1.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d27b317e207b10f69f5e75494119e391a96f48861ae870d1da6edac98ca900" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "lz4_flex" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" -dependencies = [ - "twox-hash", -] - -[[package]] -name = "matchers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" -dependencies = [ - "regex-automata 0.1.10", -] - -[[package]] -name = "md-5" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" -dependencies = [ - "cfg-if", - "digest", -] - -[[package]] -name = "measure_time" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbefd235b0aadd181626f281e1d684e116972988c14c264e42069d5e8a5775cc" -dependencies = [ - "instant", - "log", + "syn 2.0.71", ] [[package]] name = "memchr" -version = "2.7.2" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memmap2" -version = "0.7.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +checksum = "fe751422e4a8caa417e13c3ea66452215d7d63e19e604f4980461212f3ae1322" dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5de893c32cde5f383baa4c04c5d6dbdd735cfd4a794b0debdb2bb1b421da5ff4" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -2872,25 +2492,19 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", ] -[[package]] -name = "minimal-lexical" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" - [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" dependencies = [ "adler", ] @@ -2903,81 +2517,10 @@ checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" dependencies = [ "libc", "log", - "wasi 0.11.0+wasi-snapshot-preview1", + "wasi", "windows-sys 0.48.0", ] -[[package]] -name = "mlua" -version = "0.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9bed6bce296397a9d6a86f995dd10a547a4e6949825d45225906bdcbfe7367" -dependencies = [ - "bstr", - "erased-serde", - "futures-util", - "libloading", - "mlua-sys", - "num-traits", - "once_cell", - "rustc-hash", - "serde", - "serde-value", -] - -[[package]] -name = "mlua-luau-scheduler" -version = "0.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a13eabdbc57fa38cf0b604d98ce3431573c79a964aac56e09c16c240d36cb1bf" -dependencies = [ - "async-executor", - "blocking", - "concurrent-queue", - "derive_more", - "event-listener 4.0.3", - "futures-lite 2.3.0", - "mlua", - "rustc-hash", - "tracing", -] - -[[package]] -name = "mlua-sys" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16a9ba1dd2c6ac971b204262d434c24d65067038598f0638b64e5dca28d52b8" -dependencies = [ - "cc", - "cfg-if", - "luau0-src", - "pkg-config", -] - -[[package]] -name = "murmurhash32" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b" - -[[package]] -name = "native-tls" -version = "0.2.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" -dependencies = [ - "lazy_static", - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "newline-converter" version = "0.3.0" @@ -2987,69 +2530,32 @@ dependencies = [ "unicode-segmentation", ] -[[package]] -name = "nibble_vec" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a5d83df9f36fe23f0c3648c6bbb8b0298bb5f1939c8f2704431371f4b84d43" -dependencies = [ - "smallvec", -] - [[package]] name = "nix" -version = "0.26.4" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "598beaf3cc6fdd9a5dfb1630c2800c7acd31df7aaf0f565796fba2b53ca1af1b" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "memoffset 0.7.1", -] - -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "cfg-if", "cfg_aliases", "libc", + "memoffset", ] [[package]] -name = "no-std-compat" -version = "0.4.1" +name = "nondestructive" +version = "0.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c" - -[[package]] -name = "nom" -version = "7.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "df8c6dda9b7e104ebb1ff495a2c95c9c14bf9ef285328f08617ed2170c681196" dependencies = [ + "bstr", + "itoa", + "lexical-core", "memchr", - "minimal-lexical", -] - -[[package]] -name = "nonzero_ext" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21" - -[[package]] -name = "nu-ansi-term" -version = "0.46.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" -dependencies = [ - "overload", - "winapi", + "ryu", + "slab", + "twox-hash", ] [[package]] @@ -3068,9 +2574,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c165a9ab64cf766f73521c0dd2cfdff64f488b8f0b3e621face3462d3db536d7" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", @@ -3137,7 +2643,16 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", + "libc", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ "libc", ] @@ -3149,9 +2664,9 @@ checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" [[package]] name = "object" -version = "0.32.2" +version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "081b846d1d56ddfc18fdf1a922e4f6e07a11768ea1b92dec44e42b72712ccfce" dependencies = [ "memchr", ] @@ -3163,62 +2678,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] -name = "oneshot" -version = "0.1.6" +name = "open" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f6640c6bda7731b1fdbab747981a0f896dd1fedaf9f4a53fa237a04a84431f4" +checksum = "61a877bf6abd716642a53ef1b89fb498923a4afca5c754f9050b4d081c05c4b3" dependencies = [ - "loom", -] - -[[package]] -name = "opaque-debug" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" - -[[package]] -name = "openssl" -version = "0.10.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "foreign-types", + "is-wsl", "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 2.0.63", -] - -[[package]] -name = "openssl-probe" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - -[[package]] -name = "openssl-sys" -version = "0.9.102" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", + "pathdiff", ] [[package]] @@ -3227,15 +2694,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-float" -version = "2.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - [[package]] name = "ordered-stream" version = "0.2.0" @@ -3246,41 +2704,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "os_info" -version = "3.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" -dependencies = [ - "log", - "serde", - "windows-sys 0.52.0", -] - -[[package]] -name = "os_str_bytes" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ac44c994af577c799b1b4bd80dc214701e349873ad894d6cdf96f4f7526e0b9" -dependencies = [ - "memchr", -] - -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - -[[package]] -name = "ownedbytes" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e8a72b918ae8198abb3a18c190288123e1d442b6b9a7d709305fd194688b4b7" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "parking" version = "2.2.0" @@ -3289,9 +2712,9 @@ checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" [[package]] name = "parking_lot" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e4af0ca4f6caed20e900d564c242b8e5d4903fdacf31d3daf527b66fe6f42fb" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" dependencies = [ "lock_api", "parking_lot_core", @@ -3305,26 +2728,9 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.1", + "redox_syscall 0.5.2", "smallvec", - "windows-targets 0.52.5", -] - -[[package]] -name = "parse-size" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "944553dd59c802559559161f9816429058b869003836120e262e8caec061b7ae" - -[[package]] -name = "password-hash" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7676374caaee8a325c9e7a2ae557f216c5563a171d6997b0ef8a65af35147700" -dependencies = [ - "base64ct", - "rand_core", - "subtle", + "windows-targets 0.52.6", ] [[package]] @@ -3333,12 +2739,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "path-clean" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17359afc20d7ab31fdb42bb844c8b3bb1dabd7dcf7e68428492da7f16966fcef" - [[package]] name = "pathdiff" version = "0.2.1" @@ -3347,14 +2747,12 @@ checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" [[package]] name = "pbkdf2" -version = "0.11.0" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83a0692ec44e4cf1ef28ca317f14f8f07da2d95ec3fa01f86e4467b725e60917" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" dependencies = [ "digest", "hmac", - "password-hash", - "sha2", ] [[package]] @@ -3365,36 +2763,37 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pesde" -version = "0.4.7" +version = "0.5.0-dev.0" dependencies = [ "anyhow", - "auth-git2", "cfg-if", "chrono", "clap", + "colored", "directories", "flate2", "full_moon", - "futures-executor", - "git2", + "gix", "ignore", "indicatif", "indicatif-log-bridge", "inquire", "keyring", "log", - "lune", + "nondestructive", "once_cell", + "open", "pathdiff", "pretty_env_logger", "relative-path", - "reqwest 0.12.4", - "semver 1.0.23", + "reqwest", + "secrecy", + "semver", "serde", "serde_json", + "serde_with", "serde_yaml", "tar", - "tempfile", "thiserror", "threadpool", "toml", @@ -3402,37 +2801,6 @@ dependencies = [ "zip", ] -[[package]] -name = "pesde-registry" -version = "0.6.1" -dependencies = [ - "actix-cors", - "actix-governor", - "actix-multipart", - "actix-multipart-derive", - "actix-web", - "actix-web-httpauth", - "dotenvy", - "flate2", - "git2", - "log", - "pesde", - "pretty_env_logger", - "reqwest 0.12.4", - "rusty-s3", - "semver 1.0.23", - "sentry", - "sentry-actix", - "sentry-log", - "serde", - "serde_json", - "serde_yaml", - "tantivy", - "tar", - "thiserror", - "zstd-sys", -] - [[package]] name = "pin-project" version = "1.1.5" @@ -3450,7 +2818,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -3467,12 +2835,12 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "ae1d5c74c9876f070d3e8fd503d748c7d974c3e48da8f41350fa5222ef9b4391" dependencies = [ "atomic-waker", - "fastrand 2.1.0", + "fastrand", "futures-io", ] @@ -3482,47 +2850,17 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" -[[package]] -name = "plist" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9d34169e64b3c7a80c8621a48adaf44e0cf62c78a9b25dd9dd35f1881a17cf9" -dependencies = [ - "base64 0.21.7", - "indexmap 2.2.6", - "line-wrap", - "quick-xml 0.31.0", - "serde", - "time 0.3.36", -] - [[package]] name = "polling" -version = "2.8.0" +version = "3.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" -dependencies = [ - "autocfg", - "bitflags 1.3.2", - "cfg-if", - "concurrent-queue", - "libc", - "log", - "pin-project-lite", - "windows-sys 0.48.0", -] - -[[package]] -name = "polling" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645493cf344456ef24219d02a768cf1fb92ddf8c92161679ae3d91b91a637be3" +checksum = "a3ed00ed3fbf728b5816498ecd316d1716eecaced9c0c8d2c5a6740ca214985b" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.4.0", "pin-project-lite", - "rustix 0.38.34", + "rustix", "tracing", "windows-sys 0.52.0", ] @@ -3551,95 +2889,79 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "865724d4dbe39d9f3dd3b52b88d859d66bcb2d6a0acfd5ea68a65fb66d4bdc1c" dependencies = [ - "env_logger 0.10.2", + "env_logger", "log", ] [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +checksum = "6d37c51ca738a55da99dc0c4a34860fd675453b8b36209178c2249bb13651284" dependencies = [ - "once_cell", - "toml_edit 0.19.15", + "toml_edit 0.21.1", ] -[[package]] -name = "proc-macro-hack" -version = "0.5.20+deprecated" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068" - [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" dependencies = [ "unicode-ident", ] [[package]] -name = "profiling" -version = "1.0.15" +name = "prodash" +version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d84d1d7a6ac92673717f9f6d1518374ef257669c24ebc5ac25d5033828be58" +checksum = "744a264d26b88a6a7e37cbad97953fa233b94d585236310bcbc88474b4092d79" + +[[package]] +name = "quinn" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4ceeeeabace7857413798eb1ffa1e9c905a9946a57d81fb69b4b71c4d8eb3ad" dependencies = [ - "profiling-procmacros", + "bytes", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "thiserror", + "tokio", + "tracing", ] [[package]] -name = "profiling-procmacros" -version = "1.0.15" +name = "quinn-proto" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8021cf59c8ec9c432cfc2526ac6b8aa508ecaf29cd415f271b8406c1b851c3fd" +checksum = "ddf517c03a109db8100448a4be38d498df8a210a99fe0e1b9eaf39e78c640efe" dependencies = [ - "quote", - "syn 2.0.63", + "bytes", + "rand", + "ring", + "rustc-hash", + "rustls", + "slab", + "thiserror", + "tinyvec", + "tracing", ] [[package]] -name = "psm" -version = "0.1.21" +name = "quinn-udp" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874" +checksum = "9096629c45860fc7fb143e125eb826b5e721e10be3263160c7d60ca832cf8c46" dependencies = [ - "cc", -] - -[[package]] -name = "quanta" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5167a477619228a0b284fac2674e3c388cba90631d7b7de620e6f1fcd08da5" -dependencies = [ - "crossbeam-utils", "libc", "once_cell", - "raw-cpuid", - "wasi 0.11.0+wasi-snapshot-preview1", - "web-sys", - "winapi", -] - -[[package]] -name = "quick-xml" -version = "0.30.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quick-xml" -version = "0.31.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" -dependencies = [ - "memchr", + "socket2", + "tracing", + "windows-sys 0.52.0", ] [[package]] @@ -3651,16 +2973,6 @@ dependencies = [ "proc-macro2", ] -[[package]] -name = "radix_trie" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c069c179fcdc6a2fe24d8d18305cf085fdbd4f922c041943e203685d6a1c58fd" -dependencies = [ - "endian-type", - "nibble_vec", -] - [[package]] name = "rand" version = "0.8.5" @@ -3688,136 +3000,9 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom 0.2.15", + "getrandom", ] -[[package]] -name = "raw-cpuid" -version = "11.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e29830cbb1290e404f24c73af91c5d8d631ce7e128691e9477556b540cd01ecd" -dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "rayon" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - -[[package]] -name = "rbx_binary" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6314dd6bf5c21d0598cdb53cf5d241aa643ba41da8b8abf7402b4a35096f03f6" -dependencies = [ - "log", - "lz4", - "profiling", - "rbx_dom_weak", - "rbx_reflection", - "rbx_reflection_database", - "thiserror", -] - -[[package]] -name = "rbx_cookie" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a61b073240f4c13b1e780a8999a113dfa28bc93f2cf9fc41c6f36e7aceb5bf" -dependencies = [ - "byteorder 0.5.3", - "cookie 0.15.2", - "dirs 1.0.5", - "log", - "plist", - "winapi", - "winreg 0.10.1", -] - -[[package]] -name = "rbx_dom_weak" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b67b56bac99849c2e3c57547b036927f71c57cf7f4d900d04e3e4ee774ec316" -dependencies = [ - "rbx_types", - "serde", -] - -[[package]] -name = "rbx_reflection" -version = "4.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d41509c991b53a7276a746a795eae2b9204f398164920f61976995b47fe1722" -dependencies = [ - "rbx_types", - "serde", - "thiserror", -] - -[[package]] -name = "rbx_reflection_database" -version = "0.2.10+roblox-607" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12e20c06fa41f7aadc79005c8354f592b2c2f4d0c61e1080ed5718dafc30aea0" -dependencies = [ - "lazy_static", - "rbx_reflection", - "rmp-serde", - "serde", -] - -[[package]] -name = "rbx_types" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca23bfd469d067d81ef14f65fe09aeddc25abcf576a889d1a7664fe021cf18c" -dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", - "blake3", - "lazy_static", - "rand", - "serde", - "thiserror", -] - -[[package]] -name = "rbx_xml" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c03f95500961c32340791d1fabd4587f6873bdbff077ecca6ae32db7960dea" -dependencies = [ - "base64 0.13.1", - "log", - "rbx_dom_weak", - "rbx_reflection", - "rbx_reflection_database", - "xml-rs", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - [[package]] name = "redox_syscall" version = "0.4.1" @@ -3829,22 +3014,11 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.1" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469052894dcb553421e483e4209ee581a45100d31b4018de03e5a7ad86374a7e" +checksum = "c82cf8cff14456045f55ec4241383baeff27af886adb72ffb2162f99911de0fd" dependencies = [ - "bitflags 2.5.0", -] - -[[package]] -name = "redox_users" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" -dependencies = [ - "getrandom 0.1.16", - "redox_syscall 0.1.57", - "rust-argon2", + "bitflags 2.6.0", ] [[package]] @@ -3853,54 +3027,39 @@ version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ - "getrandom 0.2.15", + "getrandom", "libredox", "thiserror", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.6", - "regex-syntax 0.8.3", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.3", + "regex-syntax", ] [[package]] name = "regex-syntax" -version = "0.6.29" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - -[[package]] -name = "regex-syntax" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" [[package]] name = "relative-path" @@ -3913,76 +3072,34 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37" dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.28", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper", - "system-configuration", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.4", - "winreg 0.50.0", -] - -[[package]] -name = "reqwest" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "566cafdd92868e0939d3fb961bd0dc25fcfaaed179291093b3d43e6b3150ea10" -dependencies = [ - "base64 0.22.1", + "base64", "bytes", "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2 0.4.4", - "http 1.1.0", - "http-body 1.0.0", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.3.1", - "hyper-rustls 0.26.0", - "hyper-tls", + "hyper", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", "log", "mime", "mime_guess", - "native-tls", "once_cell", "percent-encoding", "pin-project-lite", - "rustls 0.22.4", - "rustls-pemfile 2.1.2", + "quinn", + "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -3990,15 +3107,14 @@ dependencies = [ "sync_wrapper", "system-configuration", "tokio", - "tokio-native-tls", - "tokio-rustls 0.25.0", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.26.1", - "winreg 0.52.0", + "webpki-roots", + "winreg", ] [[package]] @@ -4009,57 +3125,13 @@ checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" dependencies = [ "cc", "cfg-if", - "getrandom 0.2.15", + "getrandom", "libc", "spin", "untrusted", "windows-sys 0.52.0", ] -[[package]] -name = "rmp" -version = "0.8.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" -dependencies = [ - "byteorder 1.5.0", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" -dependencies = [ - "byteorder 1.5.0", - "rmp", - "serde", -] - -[[package]] -name = "rust-argon2" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" -dependencies = [ - "base64 0.13.1", - "blake2b_simd", - "constant_time_eq 0.1.5", - "crossbeam-utils", -] - -[[package]] -name = "rust-stemmers" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e46a2036019fdb888131db7a4c847a1063a7493f971ed94ea82c67eada63ca54" -dependencies = [ - "serde", - "serde_derive", -] - [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4072,36 +3144,13 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] - [[package]] name = "rustc_version" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.23", -] - -[[package]] -name = "rustix" -version = "0.37.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.3.8", - "windows-sys 0.48.0", + "semver", ] [[package]] @@ -4110,55 +3159,34 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "errno", "libc", - "linux-raw-sys 0.4.13", + "linux-raw-sys", "windows-sys 0.52.0", ] [[package]] name = "rustls" -version = "0.21.12" +version = "0.23.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +checksum = "4828ea528154ae444e5a642dbb7d5623354030dc9822b83fd9bb79683c7399d0" dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - -[[package]] -name = "rustls" -version = "0.22.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf4ef73721ac7bcd79b2b315da7779d8fc09718c6b3d2d1b2d94850eb8c18432" -dependencies = [ - "log", + "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.3", + "rustls-webpki", "subtle", "zeroize", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d" dependencies = [ - "base64 0.22.1", + "base64", "rustls-pki-types", ] @@ -4170,72 +3198,15 @@ checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d" [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.102.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - -[[package]] -name = "rustls-webpki" -version = "0.102.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3bce581c0dd41bce533ce695a1437fa16a7ab5ac3ccfa99fe1a620a7885eabf" +checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78" dependencies = [ "ring", "rustls-pki-types", "untrusted", ] -[[package]] -name = "rustversion" -version = "1.0.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092474d1a01ea8278f69e6a358998405fae5b8b963ddaeb2b0b04a128bf1dfb0" - -[[package]] -name = "rusty-s3" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31aa883f1b986a5249641e574ca0e11ac4fb9970b009c6fbb96fedaf4fa78db8" -dependencies = [ - "base64 0.21.7", - "hmac", - "md-5", - "percent-encoding", - "quick-xml 0.30.0", - "serde", - "serde_json", - "sha2", - "time 0.3.36", - "url", - "zeroize", -] - -[[package]] -name = "rustyline" -version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "clipboard-win", - "fd-lock", - "home", - "libc", - "log", - "memchr", - "nix 0.28.0", - "radix_trie", - "unicode-segmentation", - "unicode-width", - "utf8parse", - "windows-sys 0.52.0", -] - [[package]] name = "ryu" version = "1.0.18" @@ -4251,21 +3222,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" -dependencies = [ - "windows-sys 0.52.0", -] - -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" @@ -4273,23 +3229,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] -name = "sct" -version = "0.7.1" +name = "secrecy" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +checksum = "9bd1c54ea06cfd2f6b63219704de0b9b4f72dcc2b8fdef820be6cd799780e91e" dependencies = [ - "ring", - "untrusted", + "zeroize", ] [[package]] name = "secret-service" -version = "3.0.1" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5da1a5ad4d28c03536f82f77d9f36603f5e37d8869ac98f0a750d5b5686d8d95" +checksum = "e4d35ad99a181be0a60ffcbe85d680d98f87bdc4d7644ade319b87076b9dbfd4" dependencies = [ - "aes 0.7.5", - "block-modes", + "aes", + "cbc", "futures-util", "generic-array", "hkdf", @@ -4307,7 +3262,7 @@ version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.6.0", "core-foundation", "core-foundation-sys", "libc", @@ -4324,15 +3279,6 @@ dependencies = [ "libc", ] -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - [[package]] name = "semver" version = "1.0.23" @@ -4342,192 +3288,37 @@ dependencies = [ "serde", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "sentry" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00421ed8fa0c995f07cde48ba6c89e80f2b312f74ff637326f392fbfd23abe02" -dependencies = [ - "httpdate", - "native-tls", - "reqwest 0.12.4", - "sentry-backtrace", - "sentry-contexts", - "sentry-core", - "sentry-debug-images", - "sentry-panic", - "sentry-tracing", - "tokio", - "ureq", -] - -[[package]] -name = "sentry-actix" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1986312ea8425a28299262ead2483ca8f0e167994f9239848d5718041abcd49" -dependencies = [ - "actix-web", - "futures-util", - "sentry-core", -] - -[[package]] -name = "sentry-backtrace" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a79194074f34b0cbe5dd33896e5928bbc6ab63a889bd9df2264af5acb186921e" -dependencies = [ - "backtrace", - "once_cell", - "regex", - "sentry-core", -] - -[[package]] -name = "sentry-contexts" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eba8870c5dba2bfd9db25c75574a11429f6b95957b0a78ac02e2970dd7a5249a" -dependencies = [ - "hostname", - "libc", - "os_info", - "rustc_version 0.4.0", - "sentry-core", - "uname", -] - -[[package]] -name = "sentry-core" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a75011ea1c0d5c46e9e57df03ce81f5c7f0a9e199086334a1f9c0a541e0826" -dependencies = [ - "once_cell", - "rand", - "sentry-types", - "serde", - "serde_json", -] - -[[package]] -name = "sentry-debug-images" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec2a486336559414ab66548da610da5e9626863c3c4ffca07d88f7dc71c8de8" -dependencies = [ - "findshlibs", - "once_cell", - "sentry-core", -] - -[[package]] -name = "sentry-log" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74b7261245ff17a8c48e8f3e1e96fb6b84146870121af880d53aef6a5b4f784" -dependencies = [ - "log", - "sentry-core", -] - -[[package]] -name = "sentry-panic" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eaa3ecfa3c8750c78dcfd4637cfa2598b95b52897ed184b4dc77fcf7d95060d" -dependencies = [ - "sentry-backtrace", - "sentry-core", -] - -[[package]] -name = "sentry-tracing" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f715932bf369a61b7256687c6f0554141b7ce097287e30e3f7ed6e9de82498fe" -dependencies = [ - "sentry-backtrace", - "sentry-core", - "tracing-core", - "tracing-subscriber", -] - -[[package]] -name = "sentry-types" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4519c900ce734f7a0eb7aba0869dfb225a7af8820634a7dd51449e3b093cfb7c" -dependencies = [ - "debugid", - "hex", - "rand", - "serde", - "serde_json", - "thiserror", - "time 0.3.36", - "url", - "uuid", -] - [[package]] name = "serde" -version = "1.0.201" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "780f1cebed1629e4753a1a38a3c72d30b97ec044f0aef68cb26650a3c5cf363c" +checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" dependencies = [ "serde_derive", ] -[[package]] -name = "serde-value" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" -dependencies = [ - "ordered-float", - "serde", -] - [[package]] name = "serde_derive" -version = "1.0.201" +version = "1.0.204" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" +checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.120" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "4e0d21c9a8cae1235ad58a00c11cb40d4b1e5c784f1ef2c537876ed6ffd8b7c5" dependencies = [ - "indexmap 2.2.6", "itoa", "ryu", "serde", ] -[[package]] -name = "serde_plain" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1fc6db65a611022b23a0dec6975d63fb80a302cb3388835ff02c097258d50" -dependencies = [ - "serde", -] - [[package]] name = "serde_repr" version = "0.1.19" @@ -4536,14 +3327,14 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "79e674e01f999af37c49f70a6ede167a8a60b2503e56c5599532a65baa5969a0" dependencies = [ "serde", ] @@ -4560,6 +3351,36 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_with" +version = "3.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e73139bc5ec2d45e6c5fd85be5a46949c1c39a4c18e56915f5eb4c12f975e377" +dependencies = [ + "base64", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.6", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b80d3d6b56b64335c0180e5ffde23b3c5e08c14c585b51a15bd0e95393f46703" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.71", +] + [[package]] name = "serde_yaml" version = "0.9.34+deprecated" @@ -4573,15 +3394,6 @@ dependencies = [ "unsafe-libyaml", ] -[[package]] -name = "sha1" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770" -dependencies = [ - "sha1_smol", -] - [[package]] name = "sha1" version = "0.10.6" @@ -4610,15 +3422,6 @@ dependencies = [ "digest", ] -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - [[package]] name = "shell-words" version = "1.1.0" @@ -4656,13 +3459,10 @@ dependencies = [ ] [[package]] -name = "sketches-ddsketch" -version = "0.2.2" +name = "simd-adler32" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85636c14b73d81f541e525f585c0a2109e6744e1565b5c1668e31c70c10ed65c" -dependencies = [ - "serde", -] +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "slab" @@ -4678,6 +3478,9 @@ name = "smallvec" version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +dependencies = [ + "serde", +] [[package]] name = "smol_str" @@ -4688,16 +3491,6 @@ dependencies = [ "serde", ] -[[package]] -name = "socket2" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f7916fc008ca5542385b89a3d3ce689953c143e9304a9bf8beec1de48994c0d" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "socket2" version = "0.5.7" @@ -4714,104 +3507,12 @@ version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -[[package]] -name = "spinning_top" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" -dependencies = [ - "lock_api", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "stacker" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce" -dependencies = [ - "cc", - "cfg-if", - "libc", - "psm", - "winapi", -] - -[[package]] -name = "standback" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" -dependencies = [ - "version_check", -] - [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" -[[package]] -name = "stdweb" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" -dependencies = [ - "discard", - "rustc_version 0.2.3", - "stdweb-derive", - "stdweb-internal-macros", - "stdweb-internal-runtime", - "wasm-bindgen", -] - -[[package]] -name = "stdweb-derive" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_derive", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-macros" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" -dependencies = [ - "base-x", - "proc-macro2", - "quote", - "serde", - "serde_derive", - "serde_json", - "sha1 0.6.1", - "syn 1.0.109", -] - -[[package]] -name = "stdweb-internal-runtime" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - [[package]] name = "strsim" version = "0.11.1" @@ -4820,9 +3521,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -4837,9 +3538,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.63" +version = "2.0.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf5be731623ca1a1fb7d8be6f261a3be6d3e2337b8a1f97be944d020c8fcb704" +checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" dependencies = [ "proc-macro2", "quote", @@ -4848,9 +3549,9 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +checksum = "a7065abeca94b6a8a577f9bd45aa0867a2238b74e8eb67cf10d492bc39351394" [[package]] name = "system-configuration" @@ -4873,151 +3574,11 @@ dependencies = [ "libc", ] -[[package]] -name = "tantivy" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6083cd777fa94271b8ce0fe4533772cb8110c3044bab048d20f70108329a1f2" -dependencies = [ - "aho-corasick", - "arc-swap", - "async-trait", - "base64 0.21.7", - "bitpacking", - "byteorder 1.5.0", - "census", - "crc32fast", - "crossbeam-channel", - "downcast-rs", - "fastdivide", - "fs4", - "htmlescape", - "itertools 0.11.0", - "levenshtein_automata", - "log", - "lru", - "lz4_flex", - "measure_time", - "memmap2", - "murmurhash32", - "num_cpus", - "once_cell", - "oneshot", - "rayon", - "regex", - "rust-stemmers", - "rustc-hash", - "serde", - "serde_json", - "sketches-ddsketch", - "smallvec", - "tantivy-bitpacker", - "tantivy-columnar", - "tantivy-common", - "tantivy-fst", - "tantivy-query-grammar", - "tantivy-stacker", - "tantivy-tokenizer-api", - "tempfile", - "thiserror", - "time 0.3.36", - "uuid", - "winapi", -] - -[[package]] -name = "tantivy-bitpacker" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecb164321482301f514dd582264fa67f70da2d7eb01872ccd71e35e0d96655a" -dependencies = [ - "bitpacking", -] - -[[package]] -name = "tantivy-columnar" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d85f8019af9a78b3118c11298b36ffd21c2314bd76bbcd9d12e00124cbb7e70" -dependencies = [ - "fastdivide", - "fnv", - "itertools 0.11.0", - "serde", - "tantivy-bitpacker", - "tantivy-common", - "tantivy-sstable", - "tantivy-stacker", -] - -[[package]] -name = "tantivy-common" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af4a3a975e604a2aba6b1106a04505e1e7a025e6def477fab6e410b4126471e1" -dependencies = [ - "async-trait", - "byteorder 1.5.0", - "ownedbytes", - "serde", - "time 0.3.36", -] - -[[package]] -name = "tantivy-fst" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc3c506b1a8443a3a65352df6382a1fb6a7afe1a02e871cee0d25e2c3d5f3944" -dependencies = [ - "byteorder 1.5.0", - "regex-syntax 0.6.29", - "utf8-ranges", -] - -[[package]] -name = "tantivy-query-grammar" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d39c5a03100ac10c96e0c8b07538e2ab8b17da56434ab348309b31f23fada77" -dependencies = [ - "nom", -] - -[[package]] -name = "tantivy-sstable" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0c1bb43e5e8b8e05eb8009610344dbf285f06066c844032fbb3e546b3c71df" -dependencies = [ - "tantivy-common", - "tantivy-fst", - "zstd 0.12.4", -] - -[[package]] -name = "tantivy-stacker" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2c078595413f13f218cf6f97b23dcfd48936838f1d3d13a1016e05acd64ed6c" -dependencies = [ - "murmurhash32", - "tantivy-common", -] - -[[package]] -name = "tantivy-tokenizer-api" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "347b6fb212b26d3505d224f438e3c4b827ab8bd847fe9953ad5ac6b8f9443b66" -dependencies = [ - "serde", -] - [[package]] name = "tar" -version = "0.4.40" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "cb797dad5fb5b76fcf519e702f4a589483b5ef06567f160c392832c1f5e44909" dependencies = [ "filetime", "libc", @@ -5031,8 +3592,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" dependencies = [ "cfg-if", - "fastrand 2.1.0", - "rustix 0.38.34", + "fastrand", + "rustix", "windows-sys 0.52.0", ] @@ -5045,34 +3606,24 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "terminal-prompt" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "572818b3472910acbd5dff46a3413715c18e934b071ab2ba464a7b2c2af16376" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -5094,21 +3645,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "time" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" -dependencies = [ - "const_fn", - "libc", - "standback", - "stdweb", - "time-macros 0.1.1", - "version_check", - "winapi", -] - [[package]] name = "time" version = "0.3.36" @@ -5117,11 +3653,13 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ "deranged", "itoa", + "libc", "num-conv", + "num_threads", "powerfmt", "serde", "time-core", - "time-macros 0.2.18", + "time-macros", ] [[package]] @@ -5130,16 +3668,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" -[[package]] -name = "time-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" -dependencies = [ - "proc-macro-hack", - "time-macros-impl", -] - [[package]] name = "time-macros" version = "0.2.18" @@ -5150,24 +3678,11 @@ dependencies = [ "time-core", ] -[[package]] -name = "time-macros-impl" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" -dependencies = [ - "proc-macro-hack", - "proc-macro2", - "quote", - "standback", - "syn 1.0.109", -] - [[package]] name = "tinyvec" -version = "1.6.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" dependencies = [ "tinyvec_macros", ] @@ -5180,82 +3695,30 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", - "parking_lot", "pin-project-lite", - "signal-hook-registry", - "socket2 0.5.7", - "tokio-macros", - "tracing", + "socket2", "windows-sys 0.48.0", ] -[[package]] -name = "tokio-macros" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.63", -] - -[[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.24.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.21.12", - "tokio", -] - -[[package]] -name = "tokio-rustls" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "775e0c0f0adb3a2f22a00c4745d728b479985fc15ee7ca6a2608388c5569860f" -dependencies = [ - "rustls 0.22.4", + "rustls", "rustls-pki-types", "tokio", ] -[[package]] -name = "tokio-tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" -dependencies = [ - "futures-util", - "log", - "rustls 0.22.4", - "rustls-pki-types", - "tokio", - "tokio-rustls 0.25.0", - "tungstenite", - "webpki-roots 0.26.1", -] - [[package]] name = "tokio-util" version = "0.7.11" @@ -5271,31 +3734,30 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.12" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" dependencies = [ - "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.12", + "toml_edit 0.22.15", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" dependencies = [ "serde", ] [[package]] name = "toml_edit" -version = "0.19.15" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +checksum = "6a8534fd7f78b5405e860340ad6575217ce99f38d4d5c8f2442cb5ecb50090e1" dependencies = [ "indexmap 2.2.6", "toml_datetime", @@ -5304,15 +3766,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.12" +version = "0.22.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3328d4f68a705b2a4498da1d580585d39a6510f98318a2cec3018a7ec61ddef" +checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.8", + "winnow 0.6.13", ] [[package]] @@ -5328,7 +3790,6 @@ dependencies = [ "tokio", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -5349,7 +3810,6 @@ version = "0.1.40" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" dependencies = [ - "log", "pin-project-lite", "tracing-attributes", "tracing-core", @@ -5363,7 +3823,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] @@ -5373,36 +3833,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" dependencies = [ "once_cell", - "valuable", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", ] [[package]] @@ -5411,27 +3841,6 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "tungstenite" -version = "0.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" -dependencies = [ - "byteorder 1.5.0", - "bytes", - "data-encoding", - "http 1.1.0", - "httparse", - "log", - "rand", - "rustls 0.22.4", - "rustls-pki-types", - "sha1 0.10.6", - "thiserror", - "url", - "utf-8", -] - [[package]] name = "twox-hash" version = "1.6.3" @@ -5439,6 +3848,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" dependencies = [ "cfg-if", + "rand", "static_assertions", ] @@ -5454,20 +3864,11 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" dependencies = [ - "memoffset 0.9.1", + "memoffset", "tempfile", "winapi", ] -[[package]] -name = "uname" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b72f89f0ca32e4db1c04e2a72f5345d59796d4866a1ee0609084569f73683dc8" -dependencies = [ - "libc", -] - [[package]] name = "unicase" version = "2.7.0" @@ -5483,6 +3884,12 @@ version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +[[package]] +name = "unicode-bom" +version = "2.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eec5d1121208364f6793f7d2e222bf75a915c19557537745b195b253dd64217" + [[package]] name = "unicode-ident" version = "1.0.12" @@ -5506,9 +3913,9 @@ checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "0336d538f7abc86d282a4189614dfaa90810dfc2c6f6427eaf88e16311dd225d" [[package]] name = "unsafe-libyaml" @@ -5522,24 +3929,11 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "ureq" -version = "2.9.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d11a831e3c0b56e438a28308e7c810799e3c118417f342d30ecec080105395cd" -dependencies = [ - "base64 0.22.1", - "log", - "native-tls", - "once_cell", - "url", -] - [[package]] name = "url" -version = "2.5.0" +version = "2.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" dependencies = [ "form_urlencoded", "idna", @@ -5547,51 +3941,11 @@ dependencies = [ "serde", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - -[[package]] -name = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8-ranges" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" - [[package]] name = "utf8parse" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "uuid" -version = "1.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" -dependencies = [ - "getrandom 0.2.15", - "serde", -] - -[[package]] -name = "valuable" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" - -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "version_check" @@ -5599,12 +3953,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "waker-fn" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" - [[package]] name = "walkdir" version = "2.5.0" @@ -5624,12 +3972,6 @@ dependencies = [ "try-lock", ] -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5657,7 +3999,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", "wasm-bindgen-shared", ] @@ -5691,7 +4033,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5714,15 +4056,9 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "0.26.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - -[[package]] -name = "webpki-roots" -version = "0.26.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3de34ae270483955a94f4b21bdaaeb83d508bb84a01435f393818edb0012009" +checksum = "bd7c23921eeb1713a4e851530e9b9756e4fb0e89978582942612524cf09f01cd" dependencies = [ "rustls-pki-types", ] @@ -5758,32 +4094,13 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets 0.48.5", -] - -[[package]] -name = "windows" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" -dependencies = [ - "windows-core", - "windows-targets 0.52.5", -] - [[package]] name = "windows-core" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5801,7 +4118,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -5821,18 +4138,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -5843,9 +4160,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5855,9 +4172,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5867,15 +4184,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5885,9 +4202,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5897,9 +4214,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5909,9 +4226,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5921,9 +4238,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -5936,32 +4253,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.8" +version = "0.6.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3c52e9c97a68071b23e836c9380edae937f17b9c4667bd021973efc689f618d" +checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - -[[package]] -name = "winreg" -version = "0.50.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" -dependencies = [ - "cfg-if", - "windows-sys 0.48.0", -] - [[package]] name = "winreg" version = "0.52.0" @@ -5979,61 +4277,46 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8da84f1a25939b27f6820d92aed108f83ff920fdf11a7b19366c27c4cda81d4f" dependencies = [ "libc", - "linux-raw-sys 0.4.13", - "rustix 0.38.34", + "linux-raw-sys", + "rustix", ] [[package]] name = "xdg-home" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +checksum = "ca91dcf8f93db085f3a0a29358cd0b9d670915468f4290e8b85d118a34211ab8" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] -[[package]] -name = "xml-rs" -version = "0.8.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791978798f0597cfc70478424c2b4fdc2b7a8024aaff78497ef00f24ef674193" - [[package]] name = "zbus" -version = "3.15.2" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" +checksum = "851238c133804e0aa888edf4a0229481c753544ca12a60fd1c3230c8a500fe40" dependencies = [ "async-broadcast", - "async-executor", - "async-fs", - "async-io 1.13.0", - "async-lock 2.8.0", "async-process", "async-recursion", - "async-task", "async-trait", - "blocking", - "byteorder 1.5.0", - "derivative", "enumflags2", - "event-listener 2.5.3", + "event-listener", "futures-core", "futures-sink", "futures-util", "hex", - "nix 0.26.4", - "once_cell", + "nix", "ordered-stream", "rand", "serde", "serde_repr", - "sha1 0.10.6", + "sha1", "static_assertions", "tracing", "uds_windows", - "winapi", + "windows-sys 0.52.0", "xdg-home", "zbus_macros", "zbus_names", @@ -6042,23 +4325,22 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "3.15.2" +version = "4.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" +checksum = "8d5a3f12c20bd473be3194af6b49d50d7bb804ef3192dc70eddedb26b85d9da7" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "regex", - "syn 1.0.109", + "syn 2.0.71", "zvariant_utils", ] [[package]] name = "zbus_names" -version = "2.6.1" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" dependencies = [ "serde", "static_assertions", @@ -6067,111 +4349,110 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae87e3fcd617500e5d106f0380cf7b77f3c6092aae37191433159dda23cfb087" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.34" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.63", + "syn 2.0.71", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.71", +] [[package]] name = "zip" -version = "0.6.6" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "775a2b471036342aa69bc5a602bc889cb0a06cda00477d0c69566757d5553d39" dependencies = [ - "aes 0.8.4", - "byteorder 1.5.0", + "aes", + "arbitrary", "bzip2", - "constant_time_eq 0.1.5", + "constant_time_eq", "crc32fast", "crossbeam-utils", + "deflate64", + "displaydoc", "flate2", "hmac", + "indexmap 2.2.6", + "lzma-rs", + "memchr", "pbkdf2", - "sha1 0.10.6", - "time 0.3.36", - "zstd 0.11.2+zstd.1.5.2", + "rand", + "sha1", + "thiserror", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zopfli" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" +dependencies = [ + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] [[package]] name = "zstd" -version = "0.11.2+zstd.1.5.2" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20cc960326ece64f010d2d2107537f26dc589a6573a316bd5b1dba685fa5fde4" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ - "zstd-safe 5.0.2+zstd.1.5.2", -] - -[[package]] -name = "zstd" -version = "0.12.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" -dependencies = [ - "zstd-safe 6.0.6", -] - -[[package]] -name = "zstd" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffb3309596d527cfcba7dfc6ed6052f1d39dfbd7c867aa2e865e4a449c10110" -dependencies = [ - "zstd-safe 7.0.0", + "zstd-safe", ] [[package]] name = "zstd-safe" -version = "5.0.2+zstd.1.5.2" +version = "7.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d2a5585e04f9eea4b2a3d1eca508c4dee9592a89ef6f450c11719da0726f4db" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "6.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" -dependencies = [ - "libc", - "zstd-sys", -] - -[[package]] -name = "zstd-safe" -version = "7.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43747c7422e2924c11144d5229878b98180ef8b06cca4ab5af37afc8a8d8ea3e" +checksum = "fa556e971e7b568dc775c136fc9de8c779b1c2fc3a63defaafadffdbd3181afa" dependencies = [ "zstd-sys", ] [[package]] name = "zstd-sys" -version = "2.0.9+zstd.1.5.5" +version = "2.0.12+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e16efa8a874a0481a574084d34cc26fdb3b99627480f785888deb6386506656" +checksum = "0a4e40c320c3cb459d9a9ff6de98cff88f4751ee9275d140e2be94a2b74e4c13" dependencies = [ "cc", "pkg-config", @@ -6179,13 +4460,12 @@ dependencies = [ [[package]] name = "zvariant" -version = "3.15.2" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eef2be88ba09b358d3b58aca6e41cd853631d44787f319a1383ca83424fb2db" +checksum = "1724a2b330760dc7d2a8402d841119dc869ef120b139d29862d6980e9c75bfc9" dependencies = [ - "byteorder 1.5.0", + "endi", "enumflags2", - "libc", "serde", "static_assertions", "zvariant_derive", @@ -6193,24 +4473,24 @@ dependencies = [ [[package]] name = "zvariant_derive" -version = "3.15.2" +version = "4.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" +checksum = "55025a7a518ad14518fb243559c058a2e5b848b015e31f1d90414f36e3317859" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.71", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "1.0.1" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7234f0d811589db492d16893e3f21e8e2fd282e6d01b0cddee310322062cc200" +checksum = "fc242db087efc22bd9ade7aa7809e4ba828132edc312871584a6b4391bdf8786" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.71", ] diff --git a/Cargo.toml b/Cargo.toml index cf73d18..dca31ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,20 @@ [package] name = "pesde" -version = "0.4.7" +version = "0.5.0-dev.0" edition = "2021" license = "MIT" authors = ["daimond113 "] -description = "A package manager for Roblox" +description = "A package manager for Luau" homepage = "https://pesde.daimond113.com" repository = "https://github.com/daimond113/pesde" include = ["src/**/*", "Cargo.toml", "Cargo.lock", "README.md", "LICENSE", "CHANGELOG.md"] [features] -bin = ["clap", "directories", "keyring", "anyhow", "ignore", "pretty_env_logger", "reqwest/json", "reqwest/multipart", "lune", "futures-executor", "indicatif", "auth-git2", "indicatif-log-bridge", "inquire", "once_cell"] -wally = ["toml", "zip"] +bin = ["clap", "directories", "ignore", "pretty_env_logger", "reqwest/json", "reqwest/multipart", "indicatif", "indicatif-log-bridge", "inquire", "nondestructive", "colored", "anyhow", "keyring", "open"] +wally-compat = ["toml", "zip"] +roblox = [] +lune = [] +luau = [] [[bin]] name = "pesde" @@ -19,51 +22,46 @@ path = "src/main.rs" required-features = ["bin"] [dependencies] -serde = { version = "1.0.197", features = ["derive"] } -serde_yaml = "0.9.33" -serde_json = "1.0.114" -git2 = "0.18.3" -semver = { version = "1.0.22", features = ["serde"] } -reqwest = { version = "0.12.1", default-features = false, features = ["rustls-tls", "blocking"] } -tar = "0.4.40" -flate2 = "1.0.28" +serde = { version = "1.0.204", features = ["derive"] } +serde_yaml = "0.9.34" +serde_json = "1.0.120" +serde_with = "3.8.3" +gix = { version = "0.63.0", default-features = false, features = ["blocking-http-transport-reqwest-rust-tls", "revparse-regex", "credentials", "serde"] } +semver = { version = "1.0.23", features = ["serde"] } +reqwest = { version = "0.12.5", default-features = false, features = ["rustls-tls", "blocking"] } +tar = "0.4.41" +flate2 = "1.0.30" pathdiff = "0.2.1" -relative-path = { version = "1.9.2", features = ["serde"] } -log = "0.4.21" -thiserror = "1.0.58" +relative-path = { version = "1.9.3", features = ["serde"] } +log = "0.4.22" +thiserror = "1.0.62" threadpool = "1.8.1" -full_moon = { version = "0.19.0", features = ["stacker", "roblox"] } -url = { version = "2.5.0", features = ["serde"] } +full_moon = { version = "1.0.0-rc.5", features = ["luau"] } +url = { version = "2.5.2", features = ["serde"] } cfg-if = "1.0.0" +once_cell = "1.19.0" +secrecy = "0.8.0" +chrono = { version = "0.4.38", features = ["serde"] } -toml = { version = "0.8.12", optional = true } -zip = { version = "0.6.6", optional = true } +toml = { version = "0.8.14", optional = true } +zip = { version = "2.1.3", optional = true } -# chrono-lc breaks because of https://github.com/chronotope/chrono/compare/v0.4.34...v0.4.35#diff-67de5678fb5c14378bbff7ecf7f8bfab17cc223c4726f8da3afca183a4e59543 -chrono = { version = "=0.4.34", features = ["serde"] } - -clap = { version = "4.5.3", features = ["derive"], optional = true } +anyhow = { version = "1.0.86", optional = true } +open = { version = "5.3.0", optional = true } +keyring = { version = "3.0.1", features = ["crypto-rust", "windows-native", "apple-native", "linux-native"], optional = true } +colored = { version = "2.1.0", optional = true } +nondestructive = { version = "0.0.25", optional = true } +clap = { version = "4.5.9", features = ["derive"], optional = true } directories = { version = "5.0.1", optional = true } -keyring = { version = "2.3.2", optional = true } -anyhow = { version = "1.0.81", optional = true } ignore = { version = "0.4.22", optional = true } pretty_env_logger = { version = "0.5.0", optional = true } -lune = { version = "0.8.2", optional = true } -futures-executor = { version = "0.3.30", optional = true } indicatif = { version = "0.17.8", optional = true } -auth-git2 = { version = "0.5.4", optional = true } indicatif-log-bridge = { version = "0.2.2", optional = true } -inquire = { version = "0.7.3", optional = true } -once_cell = { version = "1.19.0", optional = true } - -[dev-dependencies] -tempfile = "3.10.1" +inquire = { version = "0.7.5", optional = true } [workspace] resolver = "2" -members = [ - "registry" -] +members = [] [profile.dev.package.full_moon] -opt-level = 3 \ No newline at end of file +opt-level = 3 diff --git a/README.md b/README.md index a4e0277..8b0d32d 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,10 @@
+# Important + +> pesde is currently being rewritten, and this new version is not yet ready for use. You can find the stable version in the `master` branch. + pesde is a package manager for Roblox that is designed to be feature-rich and easy to use. Currently, pesde is in a very early stage of development, but already supports the following features: @@ -17,7 +21,8 @@ Currently, pesde is in a very early stage of development, but already supports t ## Installation -pesde can be installed from GitHub Releases. You can find the latest release [here](https://github.com/daimond113/pesde/releases). +pesde can be installed from GitHub Releases. You can find the latest +release [here](https://github.com/daimond113/pesde/releases). It can also be installed by using [Aftman](https://github.com/LPGhatguy/aftman). ## Usage @@ -52,13 +57,16 @@ pesde run daimond113/pesde -- --help ## Preparing to publish -To publish you must first initialize a new project with `pesde init`. You can then use the other commands to manipulate dependencies, and edit the file +To publish you must first initialize a new project with `pesde init`. You can then use the other commands to manipulate +dependencies, and edit the file manually to add metadata such as authors, description, and license. > **Warning** -> The pesde CLI respects the `.gitignore` file and will not include files that are ignored. The `.pesdeignore` file has more power over the `.gitignore` file, so you can unignore files by prepending a `!` to the pattern. +> The pesde CLI respects the `.gitignore` file and will not include files that are ignored. The `.pesdeignore` file has +> more power over the `.gitignore` file, so you can unignore files by prepending a `!` to the pattern. -The pesde CLI supports the `.pesdeignore` file, which is similar to `.gitignore`. It can be used to include or exclude files from the package. +The pesde CLI supports the `.pesdeignore` file, which is similar to `.gitignore`. It can be used to include or exclude +files from the package. ## Documentation @@ -70,9 +78,11 @@ The main pesde registry is hosted on [fly.io](https://fly.io). You can find it a ### Self-hosting -You can self-host the registry by using the default implementation in the `registry` folder, or by creating your own implementation. The API +You can self-host the registry by using the default implementation in the `registry` folder, or by creating your own +implementation. The API must be compatible with the default implementation, which can be found in the `main.rs` file. ## Previous art -pesde is heavily inspired by [npm](https://www.npmjs.com/), [pnpm](https://pnpm.io/), [Wally](https://wally.run), and [Cargo](https://doc.rust-lang.org/cargo/). +pesde is heavily inspired by [npm](https://www.npmjs.com/), [pnpm](https://pnpm.io/), [Wally](https://wally.run), +and [Cargo](https://doc.rust-lang.org/cargo/). diff --git a/registry/.env.example b/registry/.env.example deleted file mode 100644 index 778c2a2..0000000 --- a/registry/.env.example +++ /dev/null @@ -1,11 +0,0 @@ -INDEX_REPO_URL=# url of the git repository to be used as the package index -S3_ENDPOINT=# endpoint of the s3 bucket -S3_BUCKET_NAME=# name of the s3 bucket -S3_REGION=# region of the s3 bucket -S3_ACCESS_KEY=# access key of the s3 bucket -S3_SECRET_KEY=# secret key of the s3 bucket -COMMITTER_GIT_NAME=# name of the committer used for index updates -COMMITTER_GIT_EMAIL=# email of the committer used for index updates -GITHUB_USERNAME=# username of github account with push access to the index repository -GITHUB_PAT=# personal access token of github account with push access to the index repository -SENTRY_URL=# optional url of sentry error tracking \ No newline at end of file diff --git a/registry/Cargo.toml b/registry/Cargo.toml deleted file mode 100644 index 4d68c15..0000000 --- a/registry/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "pesde-registry" -version = "0.6.1" -edition = "2021" - -[dependencies] -actix-web = "4.5.1" -actix-cors = "0.7.0" -actix-web-httpauth = "0.8.1" -actix-multipart = "0.6.1" -actix-multipart-derive = "0.6.1" -actix-governor = "0.5.0" -dotenvy = "0.15.7" -reqwest = { version = "0.12.1", features = ["json", "blocking"] } -rusty-s3 = "0.5.0" -serde = { version = "1.0.197", features = ["derive"] } -serde_json = "1.0.114" -serde_yaml = "0.9.33" -flate2 = "1.0.28" -tar = "0.4.40" -pesde = { path = ".." } -semver = "1.0.22" -git2 = "0.18.3" -thiserror = "1.0.58" -tantivy = "0.21.1" -log = "0.4.21" -pretty_env_logger = "0.5.0" -sentry = "0.32.2" -sentry-log = "0.32.2" -sentry-actix = "0.32.2" - -# zstd-sys v2.0.10 is broken: https://github.com/gyscos/zstd-rs/issues/268 -zstd-sys = "=2.0.9" \ No newline at end of file diff --git a/registry/src/endpoints/mod.rs b/registry/src/endpoints/mod.rs deleted file mode 100644 index 503723f..0000000 --- a/registry/src/endpoints/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod packages; -pub mod search; diff --git a/registry/src/endpoints/packages.rs b/registry/src/endpoints/packages.rs deleted file mode 100644 index 6d9d9a4..0000000 --- a/registry/src/endpoints/packages.rs +++ /dev/null @@ -1,256 +0,0 @@ -use actix_multipart::form::{bytes::Bytes, MultipartForm}; -use actix_web::{web, HttpResponse, Responder}; -use flate2::read::GzDecoder; -use log::error; -use reqwest::StatusCode; -use rusty_s3::S3Action; -use tantivy::{doc, DateTime, Term}; -use tar::Archive; - -use pesde::{ - dependencies::DependencySpecifier, index::Index, manifest::Manifest, - package_name::StandardPackageName, project::DEFAULT_INDEX_NAME, IGNORED_FOLDERS, - MANIFEST_FILE_NAME, -}; - -use crate::{commit_signature, errors, AppState, UserId, S3_EXPIRY}; - -#[derive(MultipartForm)] -pub struct CreateForm { - #[multipart(limit = "4 MiB")] - tarball: Bytes, -} - -pub async fn create_package( - form: MultipartForm, - app_state: web::Data, - user_id: web::ReqData, -) -> Result { - let bytes = form.tarball.data.as_ref().to_vec(); - let mut decoder = GzDecoder::new(bytes.as_slice()); - let mut archive = Archive::new(&mut decoder); - - let archive_entries = archive.entries()?.filter_map(|e| e.ok()); - - let mut manifest = None; - - for mut e in archive_entries { - let Ok(path) = e.path() else { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: "Attached file contains non-UTF-8 path".to_string(), - })); - }; - - let Some(path) = path.as_os_str().to_str() else { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: "Attached file contains non-UTF-8 path".to_string(), - })); - }; - - match path { - MANIFEST_FILE_NAME => { - if !e.header().entry_type().is_file() { - continue; - } - - let received_manifest: Manifest = - serde_yaml::from_reader(&mut e).map_err(errors::Errors::UserYaml)?; - - manifest = Some(received_manifest); - } - path => { - if e.header().entry_type().is_file() { - continue; - } - - if IGNORED_FOLDERS.contains(&path) { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: format!("Attached file contains forbidden directory {}", path), - })); - } - } - } - } - - let Some(manifest) = manifest else { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: format!("Attached file doesn't contain {MANIFEST_FILE_NAME}"), - })); - }; - - let (scope, name) = manifest.name.parts(); - - let entry = { - let mut index = app_state.index.lock().unwrap(); - let config = index.config()?; - - for (dependency, _) in manifest.dependencies().into_values() { - match dependency { - DependencySpecifier::Git(_) => { - if !config.git_allowed { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: "Git dependencies are not allowed on this registry".to_string(), - })); - } - } - DependencySpecifier::Registry(registry) => { - if index - .package(®istry.name.clone().into()) - .unwrap() - .is_none() - { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: format!("Dependency {} not found", registry.name), - })); - } - - if registry.index != DEFAULT_INDEX_NAME && !config.custom_registry_allowed { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: "Custom registries are not allowed on this registry".to_string(), - })); - } - } - #[allow(unreachable_patterns)] - _ => {} - }; - } - - match index.create_package_version(&manifest, &user_id.0)? { - Some(entry) => { - index.commit_and_push( - &format!("Add version {}@{}", manifest.name, manifest.version), - &commit_signature(), - )?; - - entry - } - None => { - return Ok(HttpResponse::BadRequest().json(errors::ErrorResponse { - error: format!( - "Version {} of {} already exists", - manifest.version, manifest.name - ), - })); - } - } - }; - - { - let mut search_writer = app_state.search_writer.lock().unwrap(); - let schema = search_writer.index().schema(); - let name_field = schema.get_field("name").unwrap(); - - search_writer.delete_term(Term::from_field_text( - name_field, - &manifest.name.to_string(), - )); - - search_writer.add_document( - doc!( - name_field => manifest.name.to_string(), - schema.get_field("version").unwrap() => manifest.version.to_string(), - schema.get_field("description").unwrap() => manifest.description.unwrap_or_default(), - schema.get_field("published_at").unwrap() => DateTime::from_timestamp_secs(entry.published_at.timestamp()) - ) - ).unwrap(); - - search_writer.commit().unwrap(); - } - - let url = app_state - .s3_bucket - .put_object( - Some(&app_state.s3_credentials), - &format!("{scope}-{name}-{}.tar.gz", manifest.version), - ) - .sign(S3_EXPIRY); - - app_state.reqwest_client.put(url).body(bytes).send().await?; - - Ok(HttpResponse::Ok().body(format!( - "Successfully published {}@{}", - manifest.name, manifest.version - ))) -} - -pub async fn get_package_version( - app_state: web::Data, - path: web::Path<(String, String, String)>, -) -> Result { - let (scope, name, mut version) = path.into_inner(); - - let package_name = StandardPackageName::new(&scope, &name)?; - - { - let index = app_state.index.lock().unwrap(); - - match index.package(&package_name.clone().into())? { - Some(package) => { - if version == "latest" { - version = package.last().map(|v| v.version.to_string()).unwrap(); - } else if !package.iter().any(|v| v.version.to_string() == version) { - return Ok(HttpResponse::NotFound().finish()); - } - } - None => return Ok(HttpResponse::NotFound().finish()), - } - } - - let url = app_state - .s3_bucket - .get_object( - Some(&app_state.s3_credentials), - &format!("{scope}-{name}-{version}.tar.gz"), - ) - .sign(S3_EXPIRY); - - let response = match app_state - .reqwest_client - .get(url) - .send() - .await? - .error_for_status() - { - Ok(response) => response, - Err(e) => { - if let Some(status) = e.status() { - if status == StatusCode::NOT_FOUND { - error!( - "package {}@{} not found in S3, but found in index", - package_name, version - ); - return Ok(HttpResponse::InternalServerError().finish()); - } - } - - return Err(e.into()); - } - }; - - Ok(HttpResponse::Ok().body(response.bytes().await?)) -} - -pub async fn get_package_versions( - app_state: web::Data, - path: web::Path<(String, String)>, -) -> Result { - let (scope, name) = path.into_inner(); - - let package_name = StandardPackageName::new(&scope, &name)?; - - { - let index = app_state.index.lock().unwrap(); - - match index.package(&package_name.into())? { - Some(package) => { - let versions = package - .iter() - .map(|v| (v.version.to_string(), v.published_at.timestamp())) - .collect::>(); - - Ok(HttpResponse::Ok().json(versions)) - } - None => Ok(HttpResponse::NotFound().finish()), - } - } -} diff --git a/registry/src/endpoints/search.rs b/registry/src/endpoints/search.rs deleted file mode 100644 index 7fcff38..0000000 --- a/registry/src/endpoints/search.rs +++ /dev/null @@ -1,81 +0,0 @@ -use actix_web::{web, Responder}; -use semver::Version; -use serde::Deserialize; -use serde_json::{json, Value}; -use tantivy::{query::AllQuery, DateTime, DocAddress, Order}; - -use pesde::{index::Index, package_name::StandardPackageName}; - -use crate::{errors, AppState}; - -#[derive(Deserialize)] -pub struct Query { - query: Option, -} - -pub async fn search_packages( - app_state: web::Data, - query: web::Query, -) -> Result { - let searcher = app_state.search_reader.searcher(); - let schema = searcher.schema(); - - let name = schema.get_field("name").unwrap(); - let version = schema.get_field("version").unwrap(); - let description = schema.get_field("description").unwrap(); - - let query = query.query.as_deref().unwrap_or_default().trim(); - - let query_parser = - tantivy::query::QueryParser::for_index(searcher.index(), vec![name, description]); - let query = if query.is_empty() { - Box::new(AllQuery) - } else { - query_parser.parse_query(query)? - }; - - let top_docs: Vec<(DateTime, DocAddress)> = searcher - .search( - &query, - &tantivy::collector::TopDocs::with_limit(10) - .order_by_fast_field("published_at", Order::Desc), - ) - .unwrap(); - - { - let index = app_state.index.lock().unwrap(); - - Ok(web::Json( - top_docs - .into_iter() - .map(|(published_at, doc_address)| { - let retrieved_doc = searcher.doc(doc_address).unwrap(); - let name: StandardPackageName = retrieved_doc - .get_first(name) - .and_then(|v| v.as_text()) - .and_then(|v| v.parse().ok()) - .unwrap(); - - let version: Version = retrieved_doc - .get_first(version) - .and_then(|v| v.as_text()) - .and_then(|v| v.parse().ok()) - .unwrap(); - - let entry = index - .package(&name.clone().into()) - .unwrap() - .and_then(|v| v.into_iter().find(|v| v.version == version)) - .unwrap(); - - json!({ - "name": name, - "version": version, - "description": entry.description, - "published_at": published_at.into_timestamp_secs(), - }) - }) - .collect::>(), - )) - } -} diff --git a/registry/src/errors.rs b/registry/src/errors.rs deleted file mode 100644 index 712681f..0000000 --- a/registry/src/errors.rs +++ /dev/null @@ -1,77 +0,0 @@ -use actix_web::{HttpResponse, ResponseError}; -use log::error; -use pesde::index::CreatePackageVersionError; -use serde::Serialize; -use thiserror::Error; - -#[derive(Serialize)] -pub struct ErrorResponse { - pub error: String, -} - -#[derive(Debug, Error)] -pub enum Errors { - #[error("io error")] - Io(#[from] std::io::Error), - - #[error("user yaml error")] - UserYaml(serde_yaml::Error), - - #[error("reqwest error")] - Reqwest(#[from] reqwest::Error), - - #[error("package name invalid")] - PackageName(#[from] pesde::package_name::StandardPackageNameValidationError), - - #[error("config error")] - Config(#[from] pesde::index::ConfigError), - - #[error("create package version error")] - CreatePackageVersion(#[from] CreatePackageVersionError), - - #[error("commit and push error")] - CommitAndPush(#[from] pesde::index::CommitAndPushError), - - #[error("index package error")] - IndexPackage(#[from] pesde::index::IndexPackageError), - - #[error("error parsing query")] - QueryParser(#[from] tantivy::query::QueryParserError), -} - -impl ResponseError for Errors { - fn error_response(&self) -> HttpResponse { - match self { - Errors::UserYaml(_) | Errors::PackageName(_) | Errors::QueryParser(_) => {} - Errors::CreatePackageVersion(err) => match err { - CreatePackageVersionError::MissingScopeOwnership => { - return HttpResponse::Unauthorized().json(ErrorResponse { - error: "You do not have permission to publish this scope".to_string(), - }); - } - CreatePackageVersionError::FromManifestIndexFileEntry(err) => { - return HttpResponse::BadRequest().json(ErrorResponse { - error: format!("Error in manifest: {err:?}"), - }); - } - _ => error!("{err:?}"), - }, - err => { - error!("{err:?}"); - } - } - - match self { - Errors::UserYaml(err) => HttpResponse::BadRequest().json(ErrorResponse { - error: format!("Error parsing YAML file: {err}"), - }), - Errors::PackageName(err) => HttpResponse::BadRequest().json(ErrorResponse { - error: format!("Invalid package name: {err}"), - }), - Errors::QueryParser(err) => HttpResponse::BadRequest().json(ErrorResponse { - error: format!("Error parsing query: {err}"), - }), - _ => HttpResponse::InternalServerError().finish(), - } - } -} diff --git a/registry/src/main.rs b/registry/src/main.rs deleted file mode 100644 index e2b3da2..0000000 --- a/registry/src/main.rs +++ /dev/null @@ -1,316 +0,0 @@ -use std::{fs::read_dir, sync::Mutex, time::Duration}; - -use actix_cors::Cors; -use actix_governor::{Governor, GovernorConfigBuilder, KeyExtractor, SimpleKeyExtractionError}; -use actix_web::{ - dev::ServiceRequest, - error::ErrorUnauthorized, - middleware::{Compress, Condition, Logger}, - rt::System, - web, App, Error, HttpMessage, HttpServer, -}; -use actix_web_httpauth::{extractors::bearer::BearerAuth, middleware::HttpAuthentication}; -use dotenvy::dotenv; -use git2::{Cred, Signature}; -use log::info; -use reqwest::{header::AUTHORIZATION, Client}; -use rusty_s3::{Bucket, Credentials, UrlStyle}; -use tantivy::{doc, DateTime, IndexReader, IndexWriter}; - -use pesde::{ - index::{GitIndex, Index, IndexFile}, - package_name::StandardPackageName, -}; - -mod endpoints; -mod errors; - -const S3_EXPIRY: Duration = Duration::from_secs(60 * 60); - -struct AppState { - s3_bucket: Bucket, - s3_credentials: Credentials, - reqwest_client: Client, - index: Mutex, - - search_reader: IndexReader, - search_writer: Mutex, -} - -macro_rules! get_env { - ($name:expr, "p") => { - std::env::var($name) - .expect(concat!("Environment variable `", $name, "` must be set")) - .parse() - .expect(concat!( - "Environment variable `", - $name, - "` must be a valid value" - )) - }; - ($name:expr) => { - std::env::var($name).expect(concat!("Environment variable `", $name, "` must be set")) - }; - ($name:expr, $default:expr, "p") => { - std::env::var($name) - .unwrap_or($default.to_string()) - .parse() - .expect(concat!( - "Environment variable `", - $name, - "` must a valid value" - )) - }; - ($name:expr, $default:expr) => { - std::env::var($name).unwrap_or($default.to_string()) - }; -} - -pub fn commit_signature<'a>() -> Signature<'a> { - Signature::now( - &get_env!("COMMITTER_GIT_NAME"), - &get_env!("COMMITTER_GIT_EMAIL"), - ) - .unwrap() -} - -#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] -pub struct UserId(pub u64); - -async fn validator( - req: ServiceRequest, - credentials: BearerAuth, -) -> Result { - let token = credentials.token(); - let app_state = req.app_data::>().unwrap(); - - let Ok(user_info) = app_state - .reqwest_client - .get("https://api.github.com/user") - .header(AUTHORIZATION, format!("Bearer {}", token)) - .send() - .await - .map(|r| r.json::()) - else { - return Err((ErrorUnauthorized("Failed to fetch user info"), req)); - }; - - let Ok(user_info) = user_info.await else { - return Err((ErrorUnauthorized("Failed to parse user info"), req)); - }; - - let Some(id) = user_info["id"].as_u64() else { - return Err((ErrorUnauthorized("Failed to fetch user info"), req)); - }; - - req.extensions_mut().insert(UserId(id)); - - Ok(req) -} - -#[derive(Debug, Clone)] -struct UserIdKey; - -impl KeyExtractor for UserIdKey { - type Key = UserId; - type KeyExtractionError = SimpleKeyExtractionError<&'static str>; - - fn extract(&self, req: &ServiceRequest) -> Result { - Ok(*req.extensions().get::().unwrap()) - } -} - -fn search_index(index: &GitIndex) -> (IndexReader, IndexWriter) { - let mut schema_builder = tantivy::schema::SchemaBuilder::new(); - let name = - schema_builder.add_text_field("name", tantivy::schema::TEXT | tantivy::schema::STORED); - let version = - schema_builder.add_text_field("version", tantivy::schema::TEXT | tantivy::schema::STORED); - let description = schema_builder.add_text_field("description", tantivy::schema::TEXT); - let published_at = schema_builder.add_date_field("published_at", tantivy::schema::FAST); - - let search_index = tantivy::Index::create_in_ram(schema_builder.build()); - let search_reader = search_index - .reader_builder() - .reload_policy(tantivy::ReloadPolicy::OnCommit) - .try_into() - .unwrap(); - let mut search_writer = search_index.writer(50_000_000).unwrap(); - - for entry in read_dir(index.path()).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - - if !path.is_dir() || path.file_name().is_some_and(|v| v == ".git") { - continue; - } - - let scope = path.file_name().and_then(|v| v.to_str()).unwrap(); - - for entry in read_dir(&path).unwrap() { - let entry = entry.unwrap(); - let path = entry.path(); - - if !path.is_file() || path.extension().is_some() { - continue; - } - - let package = path.file_name().and_then(|v| v.to_str()).unwrap(); - - let package_name = StandardPackageName::new(scope, package).unwrap(); - let entries: IndexFile = - serde_yaml::from_slice(&std::fs::read(&path).unwrap()).unwrap(); - let entry = entries.last().unwrap().clone(); - - search_writer - .add_document(doc!( - name => package_name.to_string(), - version => entry.version.to_string(), - description => entry.description.unwrap_or_default(), - published_at => DateTime::from_timestamp_secs(entry.published_at.timestamp()), - )) - .unwrap(); - } - } - - search_writer.commit().unwrap(); - - (search_reader, search_writer) -} - -fn main() -> std::io::Result<()> { - dotenv().ok(); - - let sentry_url = std::env::var("SENTRY_URL").ok(); - let with_sentry = sentry_url.is_some(); - - let mut log_builder = pretty_env_logger::formatted_builder(); - log_builder.parse_env(pretty_env_logger::env_logger::Env::default().default_filter_or("info")); - - if with_sentry { - let logger = sentry_log::SentryLogger::with_dest(log_builder.build()); - log::set_boxed_logger(Box::new(logger)).unwrap(); - log::set_max_level(log::LevelFilter::Info); - } else { - log_builder.try_init().unwrap(); - } - - let _guard = if let Some(sentry_url) = sentry_url { - std::env::set_var("RUST_BACKTRACE", "1"); - - Some(sentry::init(( - sentry_url, - sentry::ClientOptions { - release: sentry::release_name!(), - ..Default::default() - }, - ))) - } else { - None - }; - - let address = get_env!("ADDRESS", "127.0.0.1"); - let port: u16 = get_env!("PORT", "8080", "p"); - - let current_dir = std::env::current_dir().unwrap(); - - let index = GitIndex::new( - current_dir.join("cache"), - &get_env!("INDEX_REPO_URL", "p"), - Some(Box::new(|| { - Box::new(|_, _, _| { - let username = get_env!("GITHUB_USERNAME"); - let pat = get_env!("GITHUB_PAT"); - - Cred::userpass_plaintext(&username, &pat) - }) - })), - None, - ); - index.refresh().expect("failed to refresh index"); - - let (search_reader, search_writer) = search_index(&index); - - let app_data = web::Data::new(AppState { - s3_bucket: Bucket::new( - get_env!("S3_ENDPOINT", "p"), - UrlStyle::Path, - get_env!("S3_BUCKET_NAME"), - get_env!("S3_REGION"), - ) - .unwrap(), - s3_credentials: Credentials::new(get_env!("S3_ACCESS_KEY"), get_env!("S3_SECRET_KEY")), - reqwest_client: Client::builder() - .user_agent(concat!( - env!("CARGO_PKG_NAME"), - "/", - env!("CARGO_PKG_VERSION") - )) - .build() - .unwrap(), - index: Mutex::new(index), - - search_reader, - search_writer: Mutex::new(search_writer), - }); - - let upload_governor_config = GovernorConfigBuilder::default() - .burst_size(10) - .per_second(600) - .key_extractor(UserIdKey) - .use_headers() - .finish() - .unwrap(); - - let generic_governor_config = GovernorConfigBuilder::default() - .burst_size(50) - .per_second(10) - .use_headers() - .finish() - .unwrap(); - - info!("listening on {address}:{port}"); - - System::new().block_on(async move { - HttpServer::new(move || { - App::new() - .wrap(Condition::new(with_sentry, sentry_actix::Sentry::new())) - .wrap(Logger::default()) - .wrap(Cors::permissive()) - .wrap(Compress::default()) - .app_data(app_data.clone()) - .route("/", web::get().to(|| async { env!("CARGO_PKG_VERSION") })) - .service( - web::scope("/v0") - .route( - "/search", - web::get() - .to(endpoints::search::search_packages) - .wrap(Governor::new(&generic_governor_config)), - ) - .route( - "/packages/{scope}/{name}/versions", - web::get() - .to(endpoints::packages::get_package_versions) - .wrap(Governor::new(&generic_governor_config)), - ) - .route( - "/packages/{scope}/{name}/{version}", - web::get() - .to(endpoints::packages::get_package_version) - .wrap(Governor::new(&generic_governor_config)), - ) - .route( - "/packages", - web::post() - .to(endpoints::packages::create_package) - .wrap(Governor::new(&upload_governor_config)) - .wrap(HttpAuthentication::bearer(validator)), - ), - ) - }) - .bind((address, port))? - .run() - .await - }) -} diff --git a/src/cli/api_token.rs b/src/cli/api_token.rs deleted file mode 100644 index 952e241..0000000 --- a/src/cli/api_token.rs +++ /dev/null @@ -1,162 +0,0 @@ -use std::path::PathBuf; - -use crate::cli::DEFAULT_INDEX_DATA; -use keyring::Entry; -use once_cell::sync::Lazy; -use serde::{Deserialize, Serialize}; - -struct EnvVarApiTokenSource; - -const API_TOKEN_ENV_VAR: &str = "PESDE_API_TOKEN"; - -impl EnvVarApiTokenSource { - fn get_api_token(&self) -> anyhow::Result> { - match std::env::var(API_TOKEN_ENV_VAR) { - Ok(token) => Ok(Some(token)), - Err(std::env::VarError::NotPresent) => Ok(None), - Err(e) => Err(e.into()), - } - } -} - -static AUTH_FILE_PATH: Lazy = - Lazy::new(|| DEFAULT_INDEX_DATA.0.parent().unwrap().join("auth.yaml")); -static AUTH_FILE: Lazy = - Lazy::new( - || match std::fs::read_to_string(AUTH_FILE_PATH.to_path_buf()) { - Ok(config) => serde_yaml::from_str(&config).unwrap(), - Err(e) if e.kind() == std::io::ErrorKind::NotFound => AuthFile::default(), - Err(e) => panic!("{:?}", e), - }, - ); - -#[derive(Serialize, Deserialize, Default, Clone)] -struct AuthFile { - #[serde(default)] - api_token: Option, -} - -struct ConfigFileApiTokenSource; - -impl ConfigFileApiTokenSource { - fn get_api_token(&self) -> anyhow::Result> { - Ok(AUTH_FILE.api_token.clone()) - } - - fn set_api_token(&self, api_token: &str) -> anyhow::Result<()> { - let mut config = AUTH_FILE.clone(); - config.api_token = Some(api_token.to_string()); - - serde_yaml::to_writer( - &mut std::fs::File::create(AUTH_FILE_PATH.to_path_buf())?, - &config, - )?; - - Ok(()) - } - - fn delete_api_token(&self) -> anyhow::Result<()> { - let mut config = AUTH_FILE.clone(); - - config.api_token = None; - - serde_yaml::to_writer( - &mut std::fs::File::create(AUTH_FILE_PATH.to_path_buf())?, - &config, - )?; - - Ok(()) - } -} - -static KEYRING_ENTRY: Lazy = - Lazy::new(|| Entry::new(env!("CARGO_PKG_NAME"), "api_token").unwrap()); - -struct KeyringApiTokenSource; - -impl KeyringApiTokenSource { - fn get_api_token(&self) -> anyhow::Result> { - match KEYRING_ENTRY.get_password() { - Ok(api_token) => Ok(Some(api_token)), - Err(err) => match err { - keyring::Error::NoEntry | keyring::Error::PlatformFailure(_) => Ok(None), - _ => Err(err.into()), - }, - } - } - - fn set_api_token(&self, api_token: &str) -> anyhow::Result<()> { - KEYRING_ENTRY.set_password(api_token)?; - - Ok(()) - } - - fn delete_api_token(&self) -> anyhow::Result<()> { - KEYRING_ENTRY.delete_password()?; - - Ok(()) - } -} - -#[derive(Debug)] -pub enum ApiTokenSource { - EnvVar, - ConfigFile, - Keyring, -} - -impl ApiTokenSource { - pub fn get_api_token(&self) -> anyhow::Result> { - match self { - ApiTokenSource::EnvVar => EnvVarApiTokenSource.get_api_token(), - ApiTokenSource::ConfigFile => ConfigFileApiTokenSource.get_api_token(), - ApiTokenSource::Keyring => KeyringApiTokenSource.get_api_token(), - } - } - - pub fn set_api_token(&self, api_token: &str) -> anyhow::Result<()> { - match self { - ApiTokenSource::EnvVar => Ok(()), - ApiTokenSource::ConfigFile => ConfigFileApiTokenSource.set_api_token(api_token), - ApiTokenSource::Keyring => KeyringApiTokenSource.set_api_token(api_token), - } - } - - pub fn delete_api_token(&self) -> anyhow::Result<()> { - match self { - ApiTokenSource::EnvVar => Ok(()), - ApiTokenSource::ConfigFile => ConfigFileApiTokenSource.delete_api_token(), - ApiTokenSource::Keyring => KeyringApiTokenSource.delete_api_token(), - } - } - - fn persists(&self) -> bool { - !matches!(self, ApiTokenSource::EnvVar) - } -} - -pub static API_TOKEN_SOURCE: Lazy = Lazy::new(|| { - let sources: [ApiTokenSource; 3] = [ - ApiTokenSource::EnvVar, - ApiTokenSource::ConfigFile, - ApiTokenSource::Keyring, - ]; - - let mut valid_sources = vec![]; - - for source in sources { - match source.get_api_token() { - Ok(Some(_)) => return source, - Ok(None) => { - if source.persists() { - valid_sources.push(source); - } - } - Err(e) => { - log::error!("error getting api token: {e}"); - } - } - } - - valid_sources.pop().unwrap() -}); diff --git a/src/cli/auth.rs b/src/cli/auth.rs deleted file mode 100644 index 87725a7..0000000 --- a/src/cli/auth.rs +++ /dev/null @@ -1,111 +0,0 @@ -use clap::Subcommand; -use pesde::index::Index; -use reqwest::{header::AUTHORIZATION, Url}; - -use crate::cli::{api_token::API_TOKEN_SOURCE, send_request, DEFAULT_INDEX, REQWEST_CLIENT}; - -#[derive(Subcommand, Clone)] -pub enum AuthCommand { - /// Logs in to the registry - Login, - /// Logs out from the registry - Logout, -} - -pub fn auth_command(cmd: AuthCommand) -> anyhow::Result<()> { - match cmd { - AuthCommand::Login => { - let github_oauth_client_id = DEFAULT_INDEX.config()?.github_oauth_client_id; - - let response = send_request(REQWEST_CLIENT.post(Url::parse_with_params( - "https://github.com/login/device/code", - &[("client_id", &github_oauth_client_id)], - )?))? - .json::()?; - - println!( - "go to {} and enter the code `{}`", - response["verification_uri"], response["user_code"] - ); - - let mut time_left = response["expires_in"] - .as_i64() - .ok_or(anyhow::anyhow!("couldn't get expires_in"))?; - let interval = std::time::Duration::from_secs( - response["interval"] - .as_u64() - .ok_or(anyhow::anyhow!("couldn't get interval"))?, - ); - let device_code = response["device_code"] - .as_str() - .ok_or(anyhow::anyhow!("couldn't get device_code"))?; - - while time_left > 0 { - std::thread::sleep(interval); - time_left -= interval.as_secs() as i64; - let response = send_request(REQWEST_CLIENT.post(Url::parse_with_params( - "https://github.com/login/oauth/access_token", - &[ - ("client_id", github_oauth_client_id.as_str()), - ("device_code", device_code), - ("grant_type", "urn:ietf:params:oauth:grant-type:device_code"), - ], - )?))? - .json::()?; - - match response - .get("error") - .map(|s| { - s.as_str() - .ok_or(anyhow::anyhow!("couldn't get error as string")) - }) - .unwrap_or(Ok(""))? - { - "authorization_pending" => continue, - "slow_down" => { - std::thread::sleep(std::time::Duration::from_secs(5)); - continue; - } - "expired_token" => { - break; - } - "access_denied" => { - anyhow::bail!("access denied, re-run the login command"); - } - _ => (), - } - - if response.get("access_token").is_some() { - let access_token = response["access_token"] - .as_str() - .ok_or(anyhow::anyhow!("couldn't get access_token"))?; - - API_TOKEN_SOURCE.set_api_token(access_token)?; - - let response = send_request( - REQWEST_CLIENT - .get("https://api.github.com/user") - .header(AUTHORIZATION, format!("Bearer {access_token}")), - )? - .json::()?; - - let login = response["login"] - .as_str() - .ok_or(anyhow::anyhow!("couldn't get login"))?; - - println!("you're now logged in as {login}"); - return Ok(()); - } - } - - anyhow::bail!("code expired, please re-run the login command"); - } - AuthCommand::Logout => { - API_TOKEN_SOURCE.delete_api_token()?; - - println!("you're now logged out"); - } - } - - Ok(()) -} diff --git a/src/cli/auth/login.rs b/src/cli/auth/login.rs new file mode 100644 index 0000000..f0dd9b4 --- /dev/null +++ b/src/cli/auth/login.rs @@ -0,0 +1,181 @@ +use crate::cli::{auth::get_token_login, read_config, reqwest_client, set_token}; +use anyhow::Context; +use clap::Args; +use colored::Colorize; +use pesde::{ + errors::ManifestReadError, + source::{pesde::PesdePackageSource, PackageSource}, + Project, +}; +use serde::Deserialize; +use url::Url; + +#[derive(Debug, Args)] +pub struct LoginCommand { + /// The index to use. Defaults to `default`, or the configured default index if current directory doesn't have a manifest + #[arg(short, long)] + index: Option, +} + +#[derive(Debug, Deserialize)] +struct DeviceCodeResponse { + device_code: String, + user_code: String, + verification_uri: Url, + expires_in: u64, + interval: u64, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "snake_case", tag = "error")] +enum AccessTokenError { + AuthorizationPending, + SlowDown { interval: u64 }, + ExpiredToken, + AccessDenied, +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +enum AccessTokenResponse { + Success { access_token: String }, + + Error(AccessTokenError), +} + +impl LoginCommand { + pub fn run(self, project: Project) -> anyhow::Result<()> { + let manifest = match project.deser_manifest() { + Ok(manifest) => Some(manifest), + Err(e) => match e { + ManifestReadError::Io(e) if e.kind() == std::io::ErrorKind::NotFound => None, + e => return Err(e.into()), + }, + }; + + let index_url = match &self.index { + Some(index) => match index.parse() { + Ok(url) => Some(url), + Err(_) => None, + }, + None => match manifest { + Some(_) => None, + None => Some(read_config(project.data_dir())?.default_index), + }, + }; + + let index_url = match index_url { + Some(url) => url, + None => { + let index_name = self.index.as_deref().unwrap_or("default"); + + match manifest.unwrap().indices.get(index_name) { + Some(index) => index.clone(), + None => anyhow::bail!("Index {index_name} not found"), + } + } + }; + + let source = PesdePackageSource::new( + index_url + .as_str() + .try_into() + .context("cannot parse URL to git URL")?, + ); + source + .refresh(&project) + .context("failed to refresh index")?; + + dbg!(source.all_packages(&project).unwrap()); + + let config = source + .config(&project) + .context("failed to read index config")?; + let client_id = config.github_oauth_client_id; + + let reqwest = reqwest_client(project.data_dir())?; + let response = reqwest + .post(Url::parse_with_params( + "https://github.com/login/device/code", + &[("client_id", &client_id)], + )?) + .send() + .context("failed to send device code request")? + .json::() + .context("failed to parse device code response")?; + + println!( + "copy your one-time code: {}\npress enter to open {} in your browser...", + response.user_code.bold(), + response.verification_uri.as_str().blue() + ); + + { + let mut input = String::new(); + std::io::stdin() + .read_line(&mut input) + .context("failed to read input")?; + } + + match open::that(response.verification_uri.as_str()) { + Ok(_) => (), + Err(e) => { + eprintln!("failed to open browser: {e}"); + } + } + + let mut time_left = response.expires_in; + let mut interval = std::time::Duration::from_secs(response.interval); + + while time_left > 0 { + std::thread::sleep(interval); + time_left = time_left.saturating_sub(interval.as_secs()); + + let response = reqwest + .post(Url::parse_with_params( + "https://github.com/login/oauth/access_token", + &[ + ("client_id", &client_id), + ("device_code", &response.device_code), + ( + "grant_type", + &"urn:ietf:params:oauth:grant-type:device_code".to_string(), + ), + ], + )?) + .send() + .context("failed to send access token request")? + .json::() + .context("failed to parse access token response")?; + + match response { + AccessTokenResponse::Success { access_token } => { + set_token(project.data_dir(), Some(&access_token))?; + + println!( + "logged in as {}", + get_token_login(&reqwest, &access_token)?.bold() + ); + return Ok(()); + } + AccessTokenResponse::Error(e) => match e { + AccessTokenError::AuthorizationPending => continue, + AccessTokenError::SlowDown { + interval: new_interval, + } => { + interval = std::time::Duration::from_secs(new_interval); + continue; + } + AccessTokenError::ExpiredToken => { + break; + } + AccessTokenError::AccessDenied => { + anyhow::bail!("access denied, re-run the login command"); + } + }, + } + } + + anyhow::bail!("code expired, please re-run the login command"); + } +} diff --git a/src/cli/auth/logout.rs b/src/cli/auth/logout.rs new file mode 100644 index 0000000..a2a1727 --- /dev/null +++ b/src/cli/auth/logout.rs @@ -0,0 +1,16 @@ +use crate::cli::set_token; +use clap::Args; +use pesde::Project; + +#[derive(Debug, Args)] +pub struct LogoutCommand {} + +impl LogoutCommand { + pub fn run(self, project: Project) -> anyhow::Result<()> { + set_token(project.data_dir(), None)?; + + println!("logged out"); + + Ok(()) + } +} diff --git a/src/cli/auth/mod.rs b/src/cli/auth/mod.rs new file mode 100644 index 0000000..ad8f7f6 --- /dev/null +++ b/src/cli/auth/mod.rs @@ -0,0 +1,49 @@ +use anyhow::Context; +use clap::Subcommand; +use pesde::Project; +use serde::Deserialize; + +mod login; +mod logout; +mod whoami; + +#[derive(Debug, Deserialize)] +struct UserResponse { + login: String, +} + +pub fn get_token_login( + reqwest: &reqwest::blocking::Client, + access_token: &str, +) -> anyhow::Result { + let response = reqwest + .get("https://api.github.com/user") + .header("Authorization", format!("Bearer {access_token}")) + .send() + .context("failed to send user request")? + .json::() + .context("failed to parse user response")?; + + Ok(response.login) +} + +#[derive(Debug, Subcommand)] +pub enum AuthCommands { + /// Logs in into GitHub, and stores the token + Login(login::LoginCommand), + /// Removes the stored token + Logout(logout::LogoutCommand), + /// Prints the username of the currently logged-in user + #[clap(name = "whoami")] + WhoAmI(whoami::WhoAmICommand), +} + +impl AuthCommands { + pub fn run(self, project: Project) -> anyhow::Result<()> { + match self { + AuthCommands::Login(login) => login.run(project), + AuthCommands::Logout(logout) => logout.run(project), + AuthCommands::WhoAmI(whoami) => whoami.run(project), + } + } +} diff --git a/src/cli/auth/whoami.rs b/src/cli/auth/whoami.rs new file mode 100644 index 0000000..6a314ee --- /dev/null +++ b/src/cli/auth/whoami.rs @@ -0,0 +1,26 @@ +use crate::cli::{auth::get_token_login, get_token, reqwest_client}; +use clap::Args; +use colored::Colorize; +use pesde::Project; + +#[derive(Debug, Args)] +pub struct WhoAmICommand {} + +impl WhoAmICommand { + pub fn run(self, project: Project) -> anyhow::Result<()> { + let token = match get_token(project.data_dir())? { + Some(token) => token, + None => { + println!("not logged in"); + return Ok(()); + } + }; + + println!( + "logged in as {}", + get_token_login(&reqwest_client(project.data_dir())?, &token)?.bold() + ); + + Ok(()) + } +} diff --git a/src/cli/config.rs b/src/cli/config.rs deleted file mode 100644 index cddecb1..0000000 --- a/src/cli/config.rs +++ /dev/null @@ -1,42 +0,0 @@ -use std::path::PathBuf; - -use clap::Subcommand; - -use crate::{cli::CLI_CONFIG, CliConfig}; - -#[derive(Subcommand, Clone)] -pub enum ConfigCommand { - /// Sets the cache directory - SetCacheDir { - /// The directory to use as the cache directory - #[clap(value_name = "DIRECTORY")] - directory: Option, - }, - /// Gets the cache directory - GetCacheDir, -} - -pub fn config_command(cmd: ConfigCommand) -> anyhow::Result<()> { - match cmd { - ConfigCommand::SetCacheDir { directory } => { - let cli_config = CliConfig { - cache_dir: directory, - }; - - cli_config.write()?; - - println!( - "cache directory set to: `{}`", - cli_config.cache_dir().display() - ); - } - ConfigCommand::GetCacheDir => { - println!( - "current cache directory: `{}`", - CLI_CONFIG.cache_dir().display() - ); - } - } - - Ok(()) -} diff --git a/src/cli/config/default_index.rs b/src/cli/config/default_index.rs new file mode 100644 index 0000000..63e78ad --- /dev/null +++ b/src/cli/config/default_index.rs @@ -0,0 +1,39 @@ +use crate::cli::{read_config, write_config, CliConfig}; +use clap::Args; +use pesde::Project; + +#[derive(Debug, Args)] +pub struct DefaultIndexCommand { + /// The new index URL to set as default, don't pass any value to check the current default index + #[arg(index = 1)] + index: Option, + + /// Resets the default index to the default value + #[arg(short, long, conflicts_with = "index")] + reset: bool, +} + +impl DefaultIndexCommand { + pub fn run(self, project: Project) -> anyhow::Result<()> { + let mut config = read_config(project.data_dir())?; + + let index = if self.reset { + Some(CliConfig::default().default_index) + } else { + self.index + }; + + match index { + Some(index) => { + config.default_index = index.clone(); + write_config(project.data_dir(), &config)?; + println!("default index set to: {}", index); + } + None => { + println!("current default index: {}", config.default_index); + } + } + + Ok(()) + } +} diff --git a/src/cli/config/mod.rs b/src/cli/config/mod.rs new file mode 100644 index 0000000..ff7ff24 --- /dev/null +++ b/src/cli/config/mod.rs @@ -0,0 +1,18 @@ +use clap::Subcommand; +use pesde::Project; + +mod default_index; + +#[derive(Debug, Subcommand)] +pub enum ConfigCommands { + /// Configuration for the default index + DefaultIndex(default_index::DefaultIndexCommand), +} + +impl ConfigCommands { + pub fn run(self, project: Project) -> anyhow::Result<()> { + match self { + ConfigCommands::DefaultIndex(default_index) => default_index.run(project), + } + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index df1fafd..87eeb8f 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,295 +1,145 @@ -use crate::cli::{api_token::API_TOKEN_SOURCE, auth::AuthCommand, config::ConfigCommand}; -use auth_git2::GitAuthenticator; -use clap::{Parser, Subcommand}; -use directories::ProjectDirs; -use indicatif::MultiProgress; -use indicatif_log_bridge::LogWrapper; -use log::error; -use once_cell::sync::Lazy; -use pesde::{ - index::{GitIndex, Index}, - manifest::{Manifest, Realm}, - package_name::{PackageName, StandardPackageName}, - project::DEFAULT_INDEX_NAME, -}; -use pretty_env_logger::env_logger::Env; -use reqwest::{ - blocking::{RequestBuilder, Response}, - header::ACCEPT, -}; -use semver::{Version, VersionReq}; +use clap::Subcommand; + +use anyhow::Context; +use keyring::Entry; +use pesde::Project; use serde::{Deserialize, Serialize}; -use std::{ - fs::create_dir_all, - hash::{DefaultHasher, Hash, Hasher}, - path::PathBuf, - str::FromStr, -}; +use std::path::Path; -pub mod api_token; -pub mod auth; -pub mod config; -pub mod root; +mod auth; +mod config; -#[derive(Debug, Clone)] -pub struct VersionedPackageName>(PackageName, V); - -impl> FromStr for VersionedPackageName { - type Err = anyhow::Error; - - fn from_str(s: &str) -> Result { - let (name, version) = s.split_once('@').ok_or_else(|| { - anyhow::anyhow!("invalid package name: {s}; expected format: name@version") - })?; - - Ok(VersionedPackageName( - name.to_string().parse()?, - version.parse()?, - )) - } -} - -#[derive(Subcommand, Clone)] -pub enum Command { - /// Initializes a manifest file - Init, - - /// Adds a package to the manifest - Add { - /// The package to add - #[clap(value_name = "PACKAGE")] - package: VersionedPackageName, - - /// Whether the package is a peer dependency - #[clap(long, short)] - peer: bool, - - /// The realm of the package - #[clap(long, short)] - realm: Option, - }, - - /// Removes a package from the manifest - Remove { - /// The package to remove - #[clap(value_name = "PACKAGE")] - package: PackageName, - }, - - /// Lists outdated packages - Outdated, - - /// Installs the dependencies of the project - Install { - /// Whether to use the lockfile for resolving dependencies - #[clap(long, short)] - locked: bool, - }, - - /// Runs the `bin` export of the specified package - Run { - /// The package to run - #[clap(value_name = "PACKAGE")] - package: Option, - - /// The arguments to pass to the package - #[clap(last = true)] - args: Vec, - }, - - /// Searches for a package on the registry - Search { - /// The query to search for - #[clap(value_name = "QUERY")] - query: Option, - }, - - /// Publishes the project to the registry - Publish, - - /// Converts a `wally.toml` file to a `pesde.yaml` file - #[cfg(feature = "wally")] - Convert, - - /// Begins a new patch - Patch { - /// The package to patch - #[clap(value_name = "PACKAGE")] - package: VersionedPackageName, - }, - - /// Commits (finishes) the patch - PatchCommit { - /// The package's changed directory - #[clap(value_name = "DIRECTORY")] - dir: PathBuf, - }, - - /// Auth-related commands - Auth { - #[clap(subcommand)] - command: AuthCommand, - }, - - /// Config-related commands - Config { - #[clap(subcommand)] - command: ConfigCommand, - }, -} - -#[derive(Parser, Clone)] -#[clap(version = env!("CARGO_PKG_VERSION"))] -pub struct Cli { - #[clap(subcommand)] - pub command: Command, - - /// The directory to run the command in - #[arg(short, long, value_name = "DIRECTORY")] - pub directory: Option, -} - -#[derive(Serialize, Deserialize, Clone, Default)] +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct CliConfig { - pub cache_dir: Option, + pub default_index: url::Url, + pub token: Option, } -impl CliConfig { - pub fn cache_dir(&self) -> PathBuf { - self.cache_dir - .clone() - .unwrap_or_else(|| DIRS.cache_dir().to_path_buf()) - } - - pub fn open() -> anyhow::Result { - let cli_config_path = DIRS.config_dir().join("config.yaml"); - - if cli_config_path.exists() { - Ok(serde_yaml::from_slice(&std::fs::read(cli_config_path)?)?) - } else { - let config = CliConfig::default(); - config.write()?; - Ok(config) - } - } - - pub fn write(&self) -> anyhow::Result<()> { - let folder = DIRS.config_dir(); - create_dir_all(folder)?; - serde_yaml::to_writer( - &mut std::fs::File::create(folder.join("config.yaml"))?, - &self, - )?; - - Ok(()) - } -} - -pub fn send_request(request_builder: RequestBuilder) -> anyhow::Result { - let res = request_builder.send()?; - - match res.error_for_status_ref() { - Ok(_) => Ok(res), - Err(e) => { - error!("request failed: {e}\nbody: {}", res.text()?); - Err(e.into()) +impl Default for CliConfig { + fn default() -> Self { + Self { + default_index: "https://github.com/daimond113/pesde-index".parse().unwrap(), + token: None, } } } -pub static CLI: Lazy = Lazy::new(Cli::parse); +pub fn read_config(data_dir: &Path) -> anyhow::Result { + let config_string = match std::fs::read_to_string(data_dir.join("config.yaml")) { + Ok(config_string) => config_string, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + return Ok(CliConfig::default()); + } + Err(e) => return Err(e).context("failed to read config file"), + }; -pub static DIRS: Lazy = Lazy::new(|| { - ProjectDirs::from("com", env!("CARGO_PKG_NAME"), env!("CARGO_BIN_NAME")) - .expect("couldn't get home directory") -}); + let config = serde_yaml::from_str(&config_string).context("failed to parse config file")?; -pub static CLI_CONFIG: Lazy = Lazy::new(|| CliConfig::open().unwrap()); + Ok(config) +} -pub static CWD: Lazy = Lazy::new(|| { - CLI.directory - .clone() - .or(std::env::current_dir().ok()) - .expect("couldn't get current directory") -}); +pub fn write_config(data_dir: &Path, config: &CliConfig) -> anyhow::Result<()> { + let config_string = serde_yaml::to_string(config).context("failed to serialize config")?; + std::fs::write(data_dir.join("config.yaml"), config_string) + .context("failed to write config file")?; -pub static REQWEST_CLIENT: Lazy = Lazy::new(|| { - let mut header_map = reqwest::header::HeaderMap::new(); - header_map.insert(ACCEPT, "application/json".parse().unwrap()); - header_map.insert("X-GitHub-Api-Version", "2022-11-28".parse().unwrap()); + Ok(()) +} - if let Ok(Some(token)) = API_TOKEN_SOURCE.get_api_token() { - header_map.insert( +pub fn get_token(data_dir: &Path) -> anyhow::Result> { + match std::env::var("PESDE_TOKEN") { + Ok(token) => return Ok(Some(token)), + Err(std::env::VarError::NotPresent) => {} + Err(e) => return Err(e.into()), + } + + let config = read_config(data_dir)?; + if let Some(token) = config.token { + return Ok(Some(token)); + } + + match Entry::new("token", env!("CARGO_PKG_NAME")) { + Ok(entry) => match entry.get_password() { + Ok(token) => return Ok(Some(token)), + Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {} + Err(e) => return Err(e.into()), + }, + Err(keyring::Error::PlatformFailure(_)) => {} + Err(e) => return Err(e.into()), + } + + Ok(None) +} + +pub fn set_token(data_dir: &Path, token: Option<&str>) -> anyhow::Result<()> { + let entry = match Entry::new("token", env!("CARGO_PKG_NAME")) { + Ok(entry) => entry, + Err(e) => return Err(e.into()), + }; + + let result = if let Some(token) = token { + entry.set_password(token) + } else { + entry.delete_credential() + }; + + match result { + Ok(()) => return Ok(()), + Err(keyring::Error::PlatformFailure(_) | keyring::Error::NoEntry) => {} + Err(e) => return Err(e.into()), + } + + let mut config = read_config(data_dir)?; + config.token = token.map(|s| s.to_string()); + write_config(data_dir, &config)?; + + Ok(()) +} + +pub fn reqwest_client(data_dir: &Path) -> anyhow::Result { + let mut headers = reqwest::header::HeaderMap::new(); + if let Some(token) = get_token(data_dir)? { + headers.insert( reqwest::header::AUTHORIZATION, - format!("Bearer {token}").parse().unwrap(), + format!("Bearer {}", token) + .parse() + .context("failed to create auth header")?, ); } - reqwest::blocking::Client::builder() + headers.insert( + reqwest::header::ACCEPT, + "application/json" + .parse() + .context("failed to create accept header")?, + ); + + Ok(reqwest::blocking::Client::builder() .user_agent(concat!( env!("CARGO_PKG_NAME"), "/", env!("CARGO_PKG_VERSION") )) - .default_headers(header_map) - .build() - .unwrap() -}); - -pub static MULTI: Lazy = Lazy::new(|| { - let logger = pretty_env_logger::formatted_builder() - .parse_env(Env::default().default_filter_or("info")) - .build(); - let multi = MultiProgress::new(); - - LogWrapper::new(multi.clone(), logger).try_init().unwrap(); - - multi -}); - -pub const DEFAULT_INDEX_URL: &str = "https://github.com/daimond113/pesde-index"; -#[cfg(feature = "wally")] -pub const DEFAULT_WALLY_INDEX_URL: &str = "https://github.com/UpliftGames/wally-index"; - -pub fn index_dir(url: &str) -> PathBuf { - let mut hasher = DefaultHasher::new(); - url.hash(&mut hasher); - let hash = hasher.finish().to_string(); - - CLI_CONFIG - .cache_dir() - .join("indices") - .join(hash) - .join("index") + .default_headers(headers) + .build()?) } -pub fn clone_index(url: &str) -> GitIndex { - let index = GitIndex::new( - index_dir(url), - &url.parse().unwrap(), - Some(Box::new(|| { - Box::new(|a, b, c| { - let git_authenticator = GitAuthenticator::new(); - let config = git2::Config::open_default().unwrap(); - let mut cred = git_authenticator.credentials(&config); +#[derive(Debug, Subcommand)] +pub enum SubCommand { + /// Authentication-related commands + #[command(subcommand)] + Auth(auth::AuthCommands), - cred(a, b, c) - }) - })), - API_TOKEN_SOURCE.get_api_token().unwrap(), - ); - - index.refresh().unwrap(); - - index + /// Configuration-related commands + #[command(subcommand)] + Config(config::ConfigCommands), } -pub static DEFAULT_INDEX_DATA: Lazy<(PathBuf, String)> = Lazy::new(|| { - let manifest = Manifest::from_path(CWD.to_path_buf()) - .map(|m| m.indices.get(DEFAULT_INDEX_NAME).unwrap().clone()); - let url = &manifest.unwrap_or(DEFAULT_INDEX_URL.to_string()); - - (index_dir(url), url.clone()) -}); - -pub static DEFAULT_INDEX: Lazy = Lazy::new(|| clone_index(&DEFAULT_INDEX_DATA.1)); +impl SubCommand { + pub fn run(self, project: Project) -> anyhow::Result<()> { + match self { + SubCommand::Auth(auth) => auth.run(project), + SubCommand::Config(config) => config.run(project), + } + } +} diff --git a/src/cli/root.rs b/src/cli/root.rs deleted file mode 100644 index 4bcb2dc..0000000 --- a/src/cli/root.rs +++ /dev/null @@ -1,593 +0,0 @@ -use cfg_if::cfg_if; -use chrono::Utc; -use std::{ - collections::{BTreeMap, HashMap}, - fs::{create_dir_all, read, remove_dir_all, write}, - str::FromStr, - time::Duration, -}; - -use flate2::{write::GzEncoder, Compression}; -use futures_executor::block_on; -use ignore::{overrides::OverrideBuilder, WalkBuilder}; -use inquire::{validator::Validation, Select, Text}; -use log::debug; -use lune::Runtime; -use once_cell::sync::Lazy; -use reqwest::{header::AUTHORIZATION, Url}; -use semver::Version; -use serde_json::Value; -use tar::Builder as TarBuilder; - -use pesde::{ - dependencies::{registry::RegistryDependencySpecifier, DependencySpecifier, PackageRef}, - index::Index, - manifest::{Manifest, PathStyle, Realm}, - multithread::MultithreadedJob, - package_name::{PackageName, StandardPackageName}, - patches::{create_patch, setup_patches_repo}, - project::{InstallOptions, Project, DEFAULT_INDEX_NAME}, - DEV_PACKAGES_FOLDER, IGNORED_FOLDERS, MANIFEST_FILE_NAME, PACKAGES_FOLDER, PATCHES_FOLDER, - SERVER_PACKAGES_FOLDER, -}; - -use crate::cli::{ - clone_index, send_request, Command, CLI_CONFIG, CWD, DEFAULT_INDEX, DEFAULT_INDEX_URL, DIRS, - MULTI, REQWEST_CLIENT, -}; - -pub const MAX_ARCHIVE_SIZE: usize = 4 * 1024 * 1024; - -fn multithreaded_bar + 'static>( - job: MultithreadedJob, - len: u64, - message: String, -) -> Result<(), anyhow::Error> { - let bar = MULTI.add( - indicatif::ProgressBar::new(len) - .with_style( - indicatif::ProgressStyle::default_bar() - .template("{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}")?, - ) - .with_message(message), - ); - bar.enable_steady_tick(Duration::from_millis(100)); - - while let Ok(result) = job.progress().recv() { - result.map_err(Into::into)?; - bar.inc(1); - } - - bar.finish_with_message("done"); - - Ok(()) -} - -macro_rules! none_if_empty { - ($s:expr) => { - if $s.is_empty() { - None - } else { - Some($s) - } - }; -} - -pub fn root_command(cmd: Command) -> anyhow::Result<()> { - let mut project: Lazy = Lazy::new(|| { - let manifest = Manifest::from_path(CWD.to_path_buf()).unwrap(); - let indices = manifest - .indices - .clone() - .into_iter() - .map(|(k, v)| (k, Box::new(clone_index(&v)) as Box)) - .collect::>(); - - Project::new(CWD.to_path_buf(), CLI_CONFIG.cache_dir(), indices, manifest).unwrap() - }); - - match cmd { - Command::Install { locked } => { - for packages_folder in &[PACKAGES_FOLDER, DEV_PACKAGES_FOLDER, SERVER_PACKAGES_FOLDER] { - if let Err(e) = remove_dir_all(CWD.join(packages_folder)) { - if e.kind() != std::io::ErrorKind::NotFound { - return Err(e.into()); - } else { - debug!("no {packages_folder} folder found, skipping removal"); - } - }; - } - - let manifest = project.manifest().clone(); - let lockfile = manifest.dependency_graph(&mut project, locked)?; - - let download_job = project.download(&lockfile)?; - - multithreaded_bar( - download_job, - lockfile.children.values().map(|v| v.len() as u64).sum(), - "Downloading packages".to_string(), - )?; - - cfg_if! { - if #[cfg(feature = "wally")] { - let sourcemap_generator = manifest.sourcemap_generator.clone(); - } - } - - #[allow(unused_variables)] - let convert_job = project.convert_manifests(&lockfile, move |path| { - cfg_if! { - if #[cfg(feature = "wally")] { - if let Some(sourcemap_generator) = &sourcemap_generator { - cfg_if! { - if #[cfg(target_os = "windows")] { - std::process::Command::new("powershell") - .args(["-C", &sourcemap_generator]) - .current_dir(path) - .output() - .expect("failed to execute process"); - } else { - std::process::Command::new("sh") - .args(["-c", &sourcemap_generator]) - .current_dir(path) - .output() - .expect("failed to execute process"); - } - } - } - } - } - }); - - cfg_if! { - if #[cfg(feature = "wally")] { - multithreaded_bar( - convert_job, - lockfile.children.values().flat_map(|v| v.values()).filter(|v| matches!(v.pkg_ref, PackageRef::Git(_) | PackageRef::Wally(_))).count() as u64, - "Converting manifests".to_string(), - )?; - } else { - convert_job?; - } - } - - let project = Lazy::force_mut(&mut project); - - project.install( - InstallOptions::new() - .locked(locked) - .auto_download(false) - .lockfile(lockfile), - )?; - } - Command::Run { package, args } => { - let bin_path = if let Some(package) = package { - let lockfile = project - .lockfile()? - .ok_or(anyhow::anyhow!("lockfile not found"))?; - - let resolved_pkg = lockfile - .children - .get(&package.clone().into()) - .and_then(|versions| { - versions - .values() - .find(|pkg_ref| lockfile.root_specifier(pkg_ref).is_some()) - }) - .ok_or(anyhow::anyhow!( - "package not found in lockfile (or isn't root)" - ))?; - - let pkg_path = resolved_pkg.directory(project.path()).1; - let manifest = Manifest::from_path(&pkg_path)?; - - let Some(bin_path) = manifest.exports.bin else { - anyhow::bail!("no bin found in package"); - }; - - bin_path.to_path(pkg_path) - } else { - let manifest = project.manifest(); - let bin_path = manifest - .exports - .bin - .clone() - .ok_or(anyhow::anyhow!("no bin found in package"))?; - - bin_path.to_path(project.path()) - }; - - let mut runtime = Runtime::new().with_args(args); - - block_on(runtime.run( - bin_path.with_extension("").display().to_string(), - &read(bin_path)?, - ))?; - } - Command::Search { query } => { - let config = DEFAULT_INDEX.config()?; - let api_url = config.api(); - - let response = send_request(REQWEST_CLIENT.get(Url::parse_with_params( - &format!("{}/v0/search", api_url), - &query.map(|q| vec![("query", q)]).unwrap_or_default(), - )?))? - .json::()?; - - for package in response.as_array().unwrap() { - println!( - "{}@{}{}", - package["name"].as_str().unwrap(), - package["version"].as_str().unwrap(), - package["description"] - .as_str() - .map(|d| if d.is_empty() { - d.to_string() - } else { - format!("\n{}\n", d) - }) - .unwrap_or_default() - ); - } - } - Command::Publish => { - if project.manifest().private { - anyhow::bail!("package is private, cannot publish"); - } - - let encoder = GzEncoder::new(vec![], Compression::default()); - let mut archive = TarBuilder::new(encoder); - - let cwd = &CWD.to_path_buf(); - - let mut walk_builder = WalkBuilder::new(cwd); - walk_builder.add_custom_ignore_filename(".pesdeignore"); - let mut overrides = OverrideBuilder::new(cwd); - - for packages_folder in IGNORED_FOLDERS { - overrides.add(&format!("!{}", packages_folder))?; - } - - walk_builder.overrides(overrides.build()?); - - for entry in walk_builder.build() { - let entry = entry?; - let path = entry.path(); - let relative_path = path.strip_prefix(cwd)?; - let entry_type = entry - .file_type() - .ok_or(anyhow::anyhow!("failed to get file type"))?; - - if relative_path.as_os_str().is_empty() { - continue; - } - - if entry_type.is_file() { - archive.append_path_with_name(path, relative_path)?; - } else if entry_type.is_dir() { - archive.append_dir(relative_path, path)?; - } - } - - let archive = archive.into_inner()?.finish()?; - - if archive.len() > MAX_ARCHIVE_SIZE { - anyhow::bail!( - "archive is too big ({} bytes), max {MAX_ARCHIVE_SIZE}. aborting...", - archive.len() - ); - } - - let part = reqwest::blocking::multipart::Part::bytes(archive) - .file_name("tarball.tar.gz") - .mime_str("application/gzip")?; - - let index = project.indices().get(DEFAULT_INDEX_NAME).unwrap(); - - let mut request = REQWEST_CLIENT - .post(format!("{}/v0/packages", index.config()?.api())) - .multipart(reqwest::blocking::multipart::Form::new().part("tarball", part)); - - if let Some(token) = index.registry_auth_token() { - request = request.header(AUTHORIZATION, format!("Bearer {token}")); - } else { - request = request.header(AUTHORIZATION, ""); - } - - println!("{}", send_request(request)?.text()?); - } - Command::Patch { package } => { - let lockfile = project - .lockfile()? - .ok_or(anyhow::anyhow!("lockfile not found"))?; - - let resolved_pkg = lockfile - .children - .get(&package.0) - .and_then(|versions| versions.get(&package.1)) - .ok_or(anyhow::anyhow!("package not found in lockfile"))?; - - let dir = DIRS - .data_dir() - .join("patches") - .join(format!("{}@{}", package.0.escaped(), package.1)) - .join(Utc::now().timestamp().to_string()); - - if dir.exists() { - anyhow::bail!( - "patch already exists. remove the directory {} to create a new patch", - dir.display() - ); - } - - create_dir_all(&dir)?; - - let project = Lazy::force_mut(&mut project); - let url = resolved_pkg.pkg_ref.resolve_url(project)?; - - let index = project.indices().get(DEFAULT_INDEX_NAME).unwrap(); - - resolved_pkg.pkg_ref.download( - &REQWEST_CLIENT, - index.registry_auth_token().map(|t| t.to_string()), - url.as_ref(), - index.credentials_fn().cloned(), - &dir, - )?; - - match &resolved_pkg.pkg_ref { - PackageRef::Git(_) => {} - _ => { - setup_patches_repo(&dir)?; - } - } - - println!("done! modify the files in {} and run `{} patch-commit ` to commit the changes", dir.display(), env!("CARGO_BIN_NAME")); - } - Command::PatchCommit { dir } => { - let name = dir - .parent() - .and_then(|p| p.file_name()) - .and_then(|f| f.to_str()) - .unwrap(); - - let patch_path = project.path().join(PATCHES_FOLDER); - create_dir_all(&patch_path)?; - - let patch_path = patch_path.join(format!("{name}.patch")); - if patch_path.exists() { - anyhow::bail!( - "patch already exists. remove the file {} to create a new patch", - patch_path.display() - ); - } - - let patches = create_patch(&dir)?; - - write(&patch_path, patches)?; - - remove_dir_all(&dir)?; - - println!( - "done! to apply the patch, run `{} install`", - env!("CARGO_BIN_NAME") - ); - } - Command::Init => { - if CWD.join(MANIFEST_FILE_NAME).exists() { - anyhow::bail!("manifest already exists"); - } - - let default_name = CWD.file_name().and_then(|s| s.to_str()); - - let mut name = - Text::new("What is the name of the package?").with_validator(|name: &str| { - Ok(match StandardPackageName::from_str(name) { - Ok(_) => Validation::Valid, - Err(e) => Validation::Invalid(e.into()), - }) - }); - - if let Some(name_str) = default_name { - name = name.with_initial_value(name_str); - } - - let name = name.prompt()?; - - let path_style = - Select::new("What style of paths do you want to use?", vec!["roblox"]).prompt()?; - let path_style = match path_style { - "roblox" => PathStyle::Roblox { - place: Default::default(), - }, - _ => unreachable!(), - }; - - let description = Text::new("What is the description of the package?").prompt()?; - let license = Text::new("What is the license of the package?").prompt()?; - let authors = Text::new("Who are the authors of the package? (split using ;)") - .prompt()? - .split(';') - .map(|s| s.trim().to_string()) - .filter(|s| !s.is_empty()) - .collect::>(); - let repository = Text::new("What is the repository of the package?").prompt()?; - - let private = Select::new("Is this package private?", vec!["yes", "no"]).prompt()?; - let private = private == "yes"; - - let realm = Select::new( - "What is the realm of the package?", - vec!["shared", "server", "dev"], - ) - .prompt()?; - - let realm = match realm { - "shared" => Realm::Shared, - "server" => Realm::Server, - "dev" => Realm::Development, - _ => unreachable!(), - }; - - let manifest = Manifest { - name: name.parse()?, - version: Version::parse("0.1.0")?, - exports: Default::default(), - path_style, - private, - realm: Some(realm), - indices: BTreeMap::from([( - DEFAULT_INDEX_NAME.to_string(), - DEFAULT_INDEX_URL.to_string(), - )]), - #[cfg(feature = "wally")] - sourcemap_generator: None, - overrides: Default::default(), - - dependencies: Default::default(), - peer_dependencies: Default::default(), - description: none_if_empty!(description), - license: none_if_empty!(license), - authors: none_if_empty!(authors), - repository: none_if_empty!(repository), - }; - - manifest.write(CWD.to_path_buf())?; - } - Command::Add { - package, - realm, - peer, - } => { - let mut manifest = project.manifest().clone(); - - let specifier = match package.0.clone() { - PackageName::Standard(name) => { - DependencySpecifier::Registry(RegistryDependencySpecifier { - name, - version: package.1, - realm, - index: DEFAULT_INDEX_NAME.to_string(), - }) - } - #[cfg(feature = "wally")] - PackageName::Wally(name) => DependencySpecifier::Wally( - pesde::dependencies::wally::WallyDependencySpecifier { - name, - version: package.1, - realm, - index_url: crate::cli::DEFAULT_WALLY_INDEX_URL.parse().unwrap(), - }, - ), - }; - - fn insert_into( - deps: &mut BTreeMap, - specifier: DependencySpecifier, - name: PackageName, - ) { - macro_rules! not_taken { - ($key:expr) => { - (!deps.contains_key(&$key)).then_some($key) - }; - } - - let key = not_taken!(name.name().to_string()) - .or_else(|| not_taken!(format!("{}/{}", name.scope(), name.name()))) - .or_else(|| not_taken!(name.to_string())) - .unwrap(); - deps.insert(key, specifier); - } - - if peer { - insert_into( - &mut manifest.peer_dependencies, - specifier, - package.0.clone(), - ); - } else { - insert_into(&mut manifest.dependencies, specifier, package.0.clone()); - } - - manifest.write(CWD.to_path_buf())? - } - Command::Remove { package } => { - let mut manifest = project.manifest().clone(); - - for dependencies in [&mut manifest.dependencies, &mut manifest.peer_dependencies] { - dependencies.retain(|_, d| { - if let DependencySpecifier::Registry(registry) = d { - match &package { - PackageName::Standard(name) => ®istry.name != name, - #[cfg(feature = "wally")] - PackageName::Wally(_) => true, - } - } else { - cfg_if! { - if #[cfg(feature = "wally")] { - #[allow(clippy::collapsible_else_if)] - if let DependencySpecifier::Wally(wally) = d { - match &package { - PackageName::Standard(_) => true, - PackageName::Wally(name) => &wally.name != name, - } - } else { - true - } - } else { - true - } - } - } - }); - } - - manifest.write(project.path())? - } - Command::Outdated => { - let project = Lazy::force_mut(&mut project); - - let manifest = project.manifest().clone(); - let lockfile = manifest.dependency_graph(project, false)?; - - for (name, versions) in &lockfile.children { - for (version, resolved_pkg) in versions { - if lockfile.root_specifier(resolved_pkg).is_none() { - continue; - } - - if let PackageRef::Registry(registry) = &resolved_pkg.pkg_ref { - let latest_version = send_request(REQWEST_CLIENT.get(format!( - "{}/v0/packages/{}/{}/versions", - resolved_pkg.pkg_ref.get_index(project).config()?.api(), - registry.name.scope(), - registry.name.name() - )))? - .json::()? - .as_array() - .and_then(|a| a.last()) - .and_then(|v| v.as_str()) - .and_then(|s| s.parse::().ok()) - .ok_or(anyhow::anyhow!( - "failed to get latest version of {name}@{version}" - ))?; - - if &latest_version > version { - println!( - "{name}@{version} is outdated. latest version: {latest_version}" - ); - } - } - } - } - } - #[cfg(feature = "wally")] - Command::Convert => { - Manifest::from_path_or_convert(CWD.to_path_buf())?; - } - _ => unreachable!(), - } - - Ok(()) -} diff --git a/src/dependencies/git.rs b/src/dependencies/git.rs deleted file mode 100644 index 33bf6ce..0000000 --- a/src/dependencies/git.rs +++ /dev/null @@ -1,237 +0,0 @@ -use std::{ - fs::create_dir_all, - hash::{DefaultHasher, Hash, Hasher}, - path::Path, - sync::Arc, -}; - -use git2::{build::RepoBuilder, Repository}; -use log::{debug, error, warn}; -use semver::Version; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use url::Url; - -use crate::{ - index::{remote_callbacks, CredentialsFn}, - manifest::{update_sync_tool_files, Manifest, ManifestConvertError, Realm}, - package_name::StandardPackageName, - project::{get_index, Indices}, -}; - -/// A dependency of a package that can be downloaded from a git repository -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct GitDependencySpecifier { - /// The URL of the git repository (can be in the form of `owner/repo`, in which case it will default to GitHub) - pub repo: String, - /// The revision of the git repository to use - pub rev: String, - /// The realm of the package - #[serde(skip_serializing_if = "Option::is_none")] - pub realm: Option, -} - -/// A reference to a package that can be downloaded from a git repository -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct GitPackageRef { - /// The name of the package - pub name: StandardPackageName, - /// The version of the package - pub version: Version, - /// The URL of the git repository - pub repo_url: Url, - /// The revision of the git repository to use - pub rev: String, -} - -/// An error that occurred while downloading a git repository -#[derive(Debug, Error)] -pub enum GitDownloadError { - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while reading the manifest of the git repository - #[error("error reading manifest")] - ManifestRead(#[from] ManifestConvertError), - - /// An error that occurred because the URL is invalid - #[error("invalid URL")] - InvalidUrl(#[from] url::ParseError), - - /// An error that occurred while resolving a git dependency's manifest - #[error("error resolving git dependency manifest")] - Resolve(#[from] GitManifestResolveError), -} - -/// An error that occurred while resolving a git dependency's manifest -#[derive(Debug, Error)] -pub enum GitManifestResolveError { - /// An error that occurred because the scope and name could not be extracted from the URL - #[error("could not extract scope and name from URL: {0}")] - ScopeAndNameFromUrl(Url), - - /// An error that occurred because the package name is invalid - #[error("invalid package name")] - InvalidPackageName(#[from] crate::package_name::StandardPackageNameValidationError), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), -} - -fn to_snake_case(s: &str) -> String { - s.chars() - .enumerate() - .map(|(i, c)| { - if c.is_uppercase() { - format!("{}{}", if i == 0 { "" } else { "_" }, c.to_lowercase()) - } else if c == '-' { - "_".to_string() - } else { - c.to_string() - } - }) - .collect() -} - -pub(crate) fn manifest(path: &Path, url: &Url) -> Result { - Manifest::from_path_or_convert(path).or_else(|_| { - let (scope, name) = url - .path_segments() - .and_then(|mut s| { - let scope = s.next(); - let name = s.next(); - - if let (Some(scope), Some(name)) = (scope, name) { - Some((scope.to_string(), name.to_string())) - } else { - None - } - }) - .ok_or_else(|| GitManifestResolveError::ScopeAndNameFromUrl(url.clone()))?; - - let manifest = Manifest { - name: StandardPackageName::new( - &to_snake_case(&scope), - &to_snake_case(name.trim_end_matches(".git")), - )?, - version: Version::new(0, 1, 0), - description: None, - license: None, - authors: None, - repository: None, - exports: Default::default(), - path_style: Default::default(), - private: true, - realm: None, - indices: Default::default(), - #[cfg(feature = "wally")] - sourcemap_generator: None, - overrides: Default::default(), - - dependencies: Default::default(), - peer_dependencies: Default::default(), - }; - - manifest.write(path).unwrap(); - - update_sync_tool_files(path, manifest.name.name().to_string())?; - - Ok(manifest) - }) -} - -impl GitDependencySpecifier { - pub(crate) fn resolve( - &self, - cache_dir: &Path, - indices: &Indices, - ) -> Result<(Manifest, Url, String), GitDownloadError> { - debug!("resolving git dependency {}", self.repo); - - // should also work with ssh urls - let repo_url = if self.repo.contains(':') { - debug!("resolved git repository name to: {}", self.repo); - Url::parse(&self.repo) - } else { - debug!("assuming git repository is a name: {}", self.repo); - Url::parse(&format!("https://github.com/{}.git", &self.repo)) - }?; - - debug!("resolved git repository url to: {}", &repo_url); - - let mut hasher = DefaultHasher::new(); - repo_url.hash(&mut hasher); - self.rev.hash(&mut hasher); - let repo_hash = hasher.finish(); - - let dest = cache_dir.join("git").join(repo_hash.to_string()); - - let repo = if !dest.exists() { - create_dir_all(&dest)?; - - let mut fetch_options = git2::FetchOptions::new(); - fetch_options.remote_callbacks(remote_callbacks!(get_index(indices, None))); - - RepoBuilder::new() - .fetch_options(fetch_options) - .clone(repo_url.as_ref(), &dest)? - } else { - Repository::open(&dest)? - }; - - let obj = repo.revparse_single(&self.rev)?; - debug!("resolved git revision {} to: {}", self.rev, obj.id()); - - repo.reset(&obj, git2::ResetType::Hard, None)?; - - Ok((manifest(&dest, &repo_url)?, repo_url, obj.id().to_string())) - } -} - -impl GitPackageRef { - /// Downloads the package to the specified destination - pub fn download>( - &self, - dest: P, - credentials_fn: Option>, - ) -> Result<(), GitDownloadError> { - let mut fetch_options = git2::FetchOptions::new(); - let mut remote_callbacks = git2::RemoteCallbacks::new(); - let credentials_fn = credentials_fn.map(|f| f()); - - if let Some(credentials_fn) = credentials_fn { - debug!("authenticating this git clone with credentials"); - remote_callbacks.credentials(credentials_fn); - } else { - debug!("no credentials provided for this git clone"); - } - - fetch_options.remote_callbacks(remote_callbacks); - - let repo = RepoBuilder::new() - .fetch_options(fetch_options) - .clone(self.repo_url.as_ref(), dest.as_ref())?; - - let obj = repo.revparse_single(&self.rev)?; - - if self.rev != obj.id().to_string() { - warn!( - "git package ref {} resolved to a different revision: {}. this shouldn't happen", - self.rev, - obj.id() - ); - } - - repo.reset(&obj, git2::ResetType::Hard, None)?; - - Ok(()) - } -} diff --git a/src/dependencies/mod.rs b/src/dependencies/mod.rs deleted file mode 100644 index f246093..0000000 --- a/src/dependencies/mod.rs +++ /dev/null @@ -1,443 +0,0 @@ -use std::{ - fmt::Display, - fs::create_dir_all, - path::{Path, PathBuf}, - sync::Arc, -}; - -use cfg_if::cfg_if; -use log::debug; -use reqwest::header::AUTHORIZATION; -use semver::Version; -use serde::{de::IntoDeserializer, Deserialize, Deserializer, Serialize}; -use serde_yaml::Value; -use thiserror::Error; -use url::Url; - -use crate::{ - dependencies::{ - git::{GitDependencySpecifier, GitPackageRef}, - registry::{RegistryDependencySpecifier, RegistryPackageRef}, - resolution::RootLockfileNode, - }, - index::{CredentialsFn, Index}, - manifest::{ManifestWriteError, Realm}, - multithread::MultithreadedJob, - package_name::PackageName, - project::{get_index, get_index_by_url, InstallProjectError, Project}, -}; - -/// Git dependency related stuff -pub mod git; -/// Registry dependency related stuff -pub mod registry; -/// Resolution -pub mod resolution; -/// Wally dependency related stuff -#[cfg(feature = "wally")] -pub mod wally; - -// To improve developer experience, we resolve the type of the dependency specifier with a custom deserializer, so that the user doesn't have to specify the type of the dependency -/// A dependency of a package -#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(untagged)] -pub enum DependencySpecifier { - /// A dependency that can be downloaded from a registry - Registry(RegistryDependencySpecifier), - /// A dependency that can be downloaded from a git repository - Git(GitDependencySpecifier), - /// A dependency that can be downloaded from a wally registry - #[cfg(feature = "wally")] - Wally(wally::WallyDependencySpecifier), -} - -impl DependencySpecifier { - /// Gets the name (or repository) of the specifier - pub fn name(&self) -> String { - match self { - DependencySpecifier::Registry(registry) => registry.name.to_string(), - DependencySpecifier::Git(git) => git.repo.to_string(), - #[cfg(feature = "wally")] - DependencySpecifier::Wally(wally) => wally.name.to_string(), - } - } - - /// Gets the version (or revision) of the specifier - pub fn version(&self) -> String { - match self { - DependencySpecifier::Registry(registry) => registry.version.to_string(), - DependencySpecifier::Git(git) => git.rev.clone(), - #[cfg(feature = "wally")] - DependencySpecifier::Wally(wally) => wally.version.to_string(), - } - } - - /// Gets the realm of the specifier - pub fn realm(&self) -> Option<&Realm> { - match self { - DependencySpecifier::Registry(registry) => registry.realm.as_ref(), - DependencySpecifier::Git(git) => git.realm.as_ref(), - #[cfg(feature = "wally")] - DependencySpecifier::Wally(wally) => wally.realm.as_ref(), - } - } -} - -impl<'de> Deserialize<'de> for DependencySpecifier { - fn deserialize>(deserializer: D) -> Result { - let yaml = Value::deserialize(deserializer)?; - - let result = if yaml.get("repo").is_some() { - GitDependencySpecifier::deserialize(yaml.into_deserializer()) - .map(DependencySpecifier::Git) - } else if yaml.get("name").is_some() { - RegistryDependencySpecifier::deserialize(yaml.into_deserializer()) - .map(DependencySpecifier::Registry) - } else if yaml.get("wally").is_some() { - cfg_if! { - if #[cfg(feature = "wally")] { - wally::WallyDependencySpecifier::deserialize(yaml.into_deserializer()) - .map(DependencySpecifier::Wally) - } else { - Err(serde::de::Error::custom("wally is not enabled")) - } - } - } else { - Err(serde::de::Error::custom("invalid dependency")) - }; - - result.map_err(|e| serde::de::Error::custom(e.to_string())) - } -} - -// Here we don't use a custom deserializer, because this is exposed to the user only from the lock file, which mustn't be edited manually anyway -/// A reference to a package -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(rename_all = "snake_case", tag = "type")] -pub enum PackageRef { - /// A reference to a package that can be downloaded from a registry - Registry(RegistryPackageRef), - /// A reference to a package that can be downloaded from a git repository - Git(GitPackageRef), - /// A reference to a package that can be downloaded from a wally registry - #[cfg(feature = "wally")] - Wally(wally::WallyPackageRef), -} - -/// An error that occurred while downloading a package -#[derive(Debug, Error)] -pub enum DownloadError { - /// An error that occurred while downloading a package from a registry - #[error("error downloading package {1} from registry")] - Registry(#[source] registry::RegistryDownloadError, Box), - - /// An error that occurred while downloading a package from a git repository - #[error("error downloading package {1} from git repository")] - Git(#[source] git::GitDownloadError, Box), - - /// An error that occurred while downloading a package from a wally registry - #[cfg(feature = "wally")] - #[error("error downloading package {1} from wally registry")] - Wally(#[source] wally::WallyDownloadError, Box), - - /// A URL is required for this type of package reference - #[error("a URL is required for this type of package reference")] - UrlRequired, -} - -/// An error that occurred while resolving a URL -#[derive(Debug, Error)] -pub enum UrlResolveError { - /// An error that occurred while resolving a URL of a registry package - #[error("error resolving URL of registry package")] - Registry(#[from] registry::RegistryUrlResolveError), - - /// An error that occurred while resolving a URL of a wally package - #[cfg(feature = "wally")] - #[error("error resolving URL of wally package")] - Wally(#[from] wally::ResolveWallyUrlError), -} - -impl PackageRef { - /// Gets the name of the package - pub fn name(&self) -> PackageName { - match self { - PackageRef::Registry(registry) => PackageName::Standard(registry.name.clone()), - PackageRef::Git(git) => PackageName::Standard(git.name.clone()), - #[cfg(feature = "wally")] - PackageRef::Wally(wally) => PackageName::Wally(wally.name.clone()), - } - } - - /// Gets the version of the package - pub fn version(&self) -> &Version { - match self { - PackageRef::Registry(registry) => ®istry.version, - PackageRef::Git(git) => &git.version, - #[cfg(feature = "wally")] - PackageRef::Wally(wally) => &wally.version, - } - } - - /// Returns the URL of the index - pub fn index_url(&self) -> Option { - match self { - PackageRef::Registry(registry) => Some(registry.index_url.clone()), - PackageRef::Git(_) => None, - #[cfg(feature = "wally")] - PackageRef::Wally(wally) => Some(wally.index_url.clone()), - } - } - - /// Resolves the URL of the package - pub fn resolve_url(&self, project: &mut Project) -> Result, UrlResolveError> { - Ok(match &self { - PackageRef::Registry(registry) => Some(registry.resolve_url(project.indices())?), - PackageRef::Git(_) => None, - #[cfg(feature = "wally")] - PackageRef::Wally(wally) => { - let cache_dir = project.cache_dir().to_path_buf(); - Some(wally.resolve_url(&cache_dir, project.indices_mut())?) - } - }) - } - - /// Gets the index of the package - pub fn get_index<'a>(&self, project: &'a Project) -> &'a dyn Index { - match &self.index_url() { - Some(url) => get_index_by_url(project.indices(), url), - None => get_index(project.indices(), None), - } - } - - /// Downloads the package to the specified destination - pub fn download>( - &self, - reqwest_client: &reqwest::blocking::Client, - registry_auth_token: Option, - url: Option<&Url>, - credentials_fn: Option>, - dest: P, - ) -> Result<(), DownloadError> { - match self { - PackageRef::Registry(registry) => registry - .download( - reqwest_client, - url.ok_or(DownloadError::UrlRequired)?, - registry_auth_token, - dest, - ) - .map_err(|e| DownloadError::Registry(e, Box::new(self.clone()))), - PackageRef::Git(git) => git - .download(dest, credentials_fn) - .map_err(|e| DownloadError::Git(e, Box::new(self.clone()))), - #[cfg(feature = "wally")] - PackageRef::Wally(wally) => wally - .download( - reqwest_client, - url.ok_or(DownloadError::UrlRequired)?, - registry_auth_token, - dest, - ) - .map_err(|e| DownloadError::Wally(e, Box::new(self.clone()))), - } - } -} - -/// An error that occurred while converting a manifest -#[derive(Debug, Error)] -pub enum ConvertManifestsError { - /// An error that occurred while converting the manifest - #[error("error converting the manifest")] - Manifest(#[from] crate::manifest::ManifestConvertError), - - /// An error that occurred while converting a git dependency's manifest - #[error("error converting a git dependency's manifest")] - Git(#[from] crate::dependencies::git::GitManifestResolveError), - - /// An error that occurred while reading the sourcemap - #[error("error reading the sourcemap")] - Sourcemap(#[from] std::io::Error), - - /// An error that occurred while parsing the sourcemap - #[cfg(feature = "wally")] - #[error("error parsing the sourcemap")] - Parse(#[from] serde_json::Error), - - /// An error that occurred while writing the manifest - #[error("error writing the manifest")] - Write(#[from] ManifestWriteError), - - /// A manifest is not present in a dependency, and the wally feature is not enabled - #[cfg(not(feature = "wally"))] - #[error("wally feature is not enabled, but the manifest is not present in the dependency")] - ManifestNotPresent, -} - -impl Project { - /// Downloads the project's dependencies - pub fn download( - &mut self, - lockfile: &RootLockfileNode, - ) -> Result, InstallProjectError> { - let (job, tx) = MultithreadedJob::new(); - - for (name, versions) in lockfile.children.clone() { - for (version, resolved_package) in versions { - let (_, source) = resolved_package.directory(self.path()); - - if source.exists() { - debug!("package {name}@{version} already downloaded, skipping..."); - continue; - } - - debug!( - "downloading package {name}@{version} to {}", - source.display() - ); - - create_dir_all(&source)?; - - let reqwest_client = self.reqwest_client.clone(); - let url = resolved_package.pkg_ref.resolve_url(self)?; - let index = resolved_package.pkg_ref.get_index(self); - let registry_auth_token = index.registry_auth_token().map(|t| t.to_string()); - let credentials_fn = index.credentials_fn().cloned(); - - job.execute(&tx, move || { - resolved_package.pkg_ref.download( - &reqwest_client, - registry_auth_token, - url.as_ref(), - credentials_fn, - source, - ) - }); - } - } - - Ok(job) - } - - /// Converts the manifests of the project's dependencies - #[cfg(feature = "wally")] - pub fn convert_manifests( - &self, - lockfile: &RootLockfileNode, - generate_sourcemap: F, - ) -> MultithreadedJob { - #[derive(Deserialize)] - #[serde(rename_all = "camelCase")] - struct SourcemapNode { - #[serde(default)] - file_paths: Vec, - } - - let (job, tx) = MultithreadedJob::new(); - - let generate_sourcemap = Arc::new(generate_sourcemap); - - for versions in lockfile.children.values() { - for resolved_package in versions.values().cloned() { - let generate_sourcemap = generate_sourcemap.clone(); - let self_path = self.path().to_path_buf(); - - job.execute(&tx, move || { - let source = match &resolved_package.pkg_ref { - PackageRef::Wally(_) | PackageRef::Git(_) => { - resolved_package.directory(self_path).1 - } - _ => return Ok(()), - }; - - let mut manifest = match &resolved_package.pkg_ref { - PackageRef::Git(git) => { - crate::dependencies::git::manifest(&source, &git.repo_url)? - } - _ => crate::manifest::Manifest::from_path_or_convert(&source)?, - }; - - generate_sourcemap(source.to_path_buf()); - - let sourcemap = source.join("sourcemap.json"); - let sourcemap: SourcemapNode = if sourcemap.exists() { - serde_json::from_str(&std::fs::read_to_string(&sourcemap)?)? - } else { - log::warn!("sourcemap for {resolved_package} not found, skipping..."); - return Ok(()); - }; - - manifest.exports.lib = sourcemap - .file_paths - .into_iter() - .find(|path| { - path.extension() - .is_some_and(|ext| ext == "lua" || ext == "luau") - }) - .or_else(|| Some(relative_path::RelativePathBuf::from("true"))); - - manifest.write(&source)?; - - Ok(()) - }); - } - } - - job - } - - /// Errors if dependencies don't have manifests, enable the `wally` feature to convert them - #[cfg(not(feature = "wally"))] - pub fn convert_manifests( - &self, - lockfile: &RootLockfileNode, - _generate_sourcemap: F, - ) -> Result<(), ConvertManifestsError> { - for versions in lockfile.children.values() { - for resolved_package in versions.values() { - let source = match &resolved_package.pkg_ref { - PackageRef::Git(_) => resolved_package.directory(self.path()).1, - _ => continue, - }; - - if match &resolved_package.pkg_ref { - PackageRef::Git(git) => { - crate::dependencies::git::manifest(&source, &git.repo_url).is_err() - } - _ => crate::manifest::Manifest::from_path_or_convert(&source).is_err(), - } { - return Err(ConvertManifestsError::ManifestNotPresent); - } - } - } - - Ok(()) - } -} - -impl Display for PackageRef { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}@{}", self.name(), self.version()) - } -} - -pub(crate) fn maybe_authenticated_request( - reqwest_client: &reqwest::blocking::Client, - url: &str, - registry_auth_token: Option, -) -> reqwest::blocking::RequestBuilder { - let mut builder = reqwest_client.get(url); - debug!("sending request to {}", url); - - if let Some(token) = registry_auth_token { - let hidden_token = token - .chars() - .enumerate() - .map(|(i, c)| if i <= 8 { c } else { '*' }) - .collect::(); - debug!("with registry token {hidden_token}"); - builder = builder.header(AUTHORIZATION, format!("Bearer {token}")); - } - - builder -} diff --git a/src/dependencies/registry.rs b/src/dependencies/registry.rs deleted file mode 100644 index 1b8aa13..0000000 --- a/src/dependencies/registry.rs +++ /dev/null @@ -1,148 +0,0 @@ -use std::path::Path; - -use log::{debug, error}; -use semver::{Version, VersionReq}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use url::Url; - -use crate::{ - dependencies::maybe_authenticated_request, - manifest::Realm, - package_name::StandardPackageName, - project::{get_index_by_url, Indices, DEFAULT_INDEX_NAME}, -}; - -fn default_index_name() -> String { - DEFAULT_INDEX_NAME.to_string() -} - -/// A dependency of a package that can be downloaded from a registry -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct RegistryDependencySpecifier { - /// The name of the package - pub name: StandardPackageName, - /// The version requirement of the package - pub version: VersionReq, - /// The name of the index to use - #[serde(default = "default_index_name")] - pub index: String, - /// The realm of the package - #[serde(skip_serializing_if = "Option::is_none")] - pub realm: Option, -} - -/// A reference to a package that can be downloaded from a registry -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct RegistryPackageRef { - /// The name of the package - pub name: StandardPackageName, - /// The version of the package - pub version: Version, - /// The index URL of the package - pub index_url: Url, -} - -/// An error that occurred while downloading a package from a registry -#[derive(Debug, Error)] -pub enum RegistryDownloadError { - /// An error that occurred while interacting with reqwest - #[error("error interacting with reqwest")] - Reqwest(#[from] reqwest::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while reading the index config - #[error("error with the index config")] - IndexConfig(#[from] crate::index::ConfigError), - - /// The package was not found on the registry - #[error("package {0} not found on the registry, but found in the index")] - NotFound(StandardPackageName), - - /// The user is unauthorized to download the package - #[error("unauthorized to download package {0}")] - Unauthorized(StandardPackageName), - - /// An HTTP error occurred - #[error("http error {0}: the server responded with {1}")] - Http(reqwest::StatusCode, String), - - /// An error occurred while parsing the api URL - #[error("error parsing the API URL")] - UrlParse(#[from] url::ParseError), -} - -/// An error that occurred while resolving the url of a registry package -#[derive(Debug, Error)] -pub enum RegistryUrlResolveError { - /// An error that occurred while reading the index config - #[error("error with the index config")] - IndexConfig(#[from] crate::index::ConfigError), - - /// An error occurred while parsing the api URL - #[error("error parsing the API URL")] - UrlParse(#[from] url::ParseError), -} - -impl RegistryPackageRef { - /// Resolves the download URL of the package - pub fn resolve_url(&self, indices: &Indices) -> Result { - let index = get_index_by_url(indices, &self.index_url); - let config = index.config()?; - - let url = config - .download() - .replace("{PACKAGE_AUTHOR}", self.name.scope()) - .replace("{PACKAGE_NAME}", self.name.name()) - .replace("{PACKAGE_VERSION}", &self.version.to_string()); - - Ok(Url::parse(&url)?) - } - - /// Downloads the package to the specified destination - pub fn download>( - &self, - reqwest_client: &reqwest::blocking::Client, - url: &Url, - registry_auth_token: Option, - dest: P, - ) -> Result<(), RegistryDownloadError> { - debug!( - "downloading registry package {}@{} from {}", - self.name, self.version, url - ); - - let response = - maybe_authenticated_request(reqwest_client, url.as_str(), registry_auth_token) - .send()?; - - if !response.status().is_success() { - return match response.status() { - reqwest::StatusCode::NOT_FOUND => { - Err(RegistryDownloadError::NotFound(self.name.clone())) - } - reqwest::StatusCode::UNAUTHORIZED => { - Err(RegistryDownloadError::Unauthorized(self.name.clone())) - } - _ => Err(RegistryDownloadError::Http( - response.status(), - response.text()?, - )), - }; - } - - let bytes = response.bytes()?; - - let mut decoder = flate2::read::GzDecoder::new(bytes.as_ref()); - let mut archive = tar::Archive::new(&mut decoder); - - archive.unpack(&dest)?; - - Ok(()) - } -} diff --git a/src/dependencies/resolution.rs b/src/dependencies/resolution.rs deleted file mode 100644 index 566a675..0000000 --- a/src/dependencies/resolution.rs +++ /dev/null @@ -1,596 +0,0 @@ -use std::{ - collections::{BTreeMap, HashMap, HashSet, VecDeque}, - fmt::Display, - path::{Path, PathBuf}, -}; - -use log::debug; -use semver::{Version, VersionReq}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::{ - dependencies::{ - git::{GitDownloadError, GitPackageRef}, - registry::RegistryPackageRef, - DependencySpecifier, PackageRef, - }, - index::{Index, IndexFileEntry, IndexPackageError}, - manifest::{DependencyType, Manifest, OverrideKey, Realm}, - package_name::{PackageName, StandardPackageName}, - project::{get_index, get_index_by_url, Indices, Project, ReadLockfileError}, - DEV_PACKAGES_FOLDER, INDEX_FOLDER, PACKAGES_FOLDER, SERVER_PACKAGES_FOLDER, -}; - -/// A mapping of packages to something -pub type PackageMap = BTreeMap>; - -/// The root node of the dependency graph -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct RootLockfileNode { - /// The name of the package - pub name: StandardPackageName, - - /// Dependency overrides - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub overrides: BTreeMap, - - /// The specifiers of the root packages - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub specifiers: PackageMap<(DependencySpecifier, String)>, - - /// All nodes in the dependency graph - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub children: PackageMap, -} - -impl RootLockfileNode { - /// Returns the specifier of the root package - pub fn root_specifier( - &self, - resolved_package: &ResolvedPackage, - ) -> Option<&(DependencySpecifier, String)> { - self.specifiers - .get(&resolved_package.pkg_ref.name()) - .and_then(|versions| versions.get(resolved_package.pkg_ref.version())) - } -} - -/// A node in the dependency graph -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct ResolvedPackage { - /// The reference to the package - pub pkg_ref: PackageRef, - /// The dependencies of the package - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub dependencies: BTreeMap, - /// The realm of the package - pub realm: Realm, - /// The type of the dependency - #[serde(default, skip_serializing_if = "crate::is_default")] - pub dep_type: DependencyType, -} - -impl Display for ResolvedPackage { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.pkg_ref) - } -} - -pub(crate) fn packages_folder<'a>(realm: Realm) -> &'a str { - match realm { - Realm::Shared => PACKAGES_FOLDER, - Realm::Server => SERVER_PACKAGES_FOLDER, - Realm::Development => DEV_PACKAGES_FOLDER, - } -} - -impl ResolvedPackage { - pub(crate) fn packages_folder(&self) -> &str { - packages_folder(self.realm) - } - - /// Returns the directory of the package in the project, and the parent of the directory - pub fn directory>(&self, project_path: P) -> (PathBuf, PathBuf) { - let name = self.pkg_ref.name().escaped(); - let container_path = project_path - .as_ref() - .join(self.packages_folder()) - .join(INDEX_FOLDER) - .join(&name) - .join(self.pkg_ref.version().to_string()); - - (container_path.clone(), container_path.join(&name)) - } -} - -macro_rules! find_highest { - ($iter:expr, $version:expr) => { - $iter - .filter(|v| $version.matches(v)) - .max_by(|a, b| a.cmp(&b)) - .cloned() - }; -} - -fn find_version_from_index( - root: &mut RootLockfileNode, - index: &dyn Index, - specifier: &DependencySpecifier, - name: PackageName, - version_req: &VersionReq, -) -> Result { - let index_entries = index - .package(&name) - .map_err(|e| ResolveError::IndexPackage(e, name.to_string()))? - .ok_or_else(|| ResolveError::PackageNotFound(name.to_string()))?; - - let resolved_versions = root.children.entry(name).or_default(); - - // try to find the highest already downloaded version that satisfies the requirement, otherwise find the highest satisfying version in the index - let Some(version) = find_highest!(resolved_versions.keys(), version_req) - .or_else(|| find_highest!(index_entries.iter().map(|v| &v.version), version_req)) - else { - return Err(ResolveError::NoSatisfyingVersion(Box::new( - specifier.clone(), - ))); - }; - - Ok(index_entries - .into_iter() - .find(|e| e.version.eq(&version)) - .unwrap()) -} - -fn find_realm(a: &Realm, b: &Realm) -> Realm { - if a == b { - return *a; - } - - Realm::Shared -} - -/// An error that occurred while resolving dependencies -#[derive(Debug, Error)] -pub enum ResolveError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred because a registry dependency conflicts with a git dependency - #[error("registry dependency {0}@{1} conflicts with git dependency")] - RegistryConflict(String, Version), - - /// An error that occurred because a git dependency conflicts with a registry dependency - #[error("git dependency {0}@{1} conflicts with registry dependency")] - GitConflict(String, Version), - - /// An error that occurred because no satisfying version was found for a dependency - #[error("no satisfying version found for dependency {0:?}")] - NoSatisfyingVersion(Box), - - /// An error that occurred while downloading a package from a git repository - #[error("error downloading git package")] - GitDownload(#[from] GitDownloadError), - - /// An error that occurred because a package was not found in the index - #[error("package {0} not found in index")] - PackageNotFound(String), - - /// An error that occurred while getting a package from the index - #[error("failed to get package {1} from index")] - IndexPackage(#[source] IndexPackageError, String), - - /// An error that occurred while reading the lockfile - #[error("failed to read lockfile")] - LockfileRead(#[from] ReadLockfileError), - - /// An error that occurred because the lockfile is out of date - #[error("out of date lockfile")] - OutOfDateLockfile, - - /// An error that occurred because two realms are incompatible - #[error("incompatible realms for package {0} (package specified {1}, user specified {2})")] - IncompatibleRealms(String, Realm, Realm), - - /// An error that occurred because a peer dependency is not installed - #[error("peer dependency {0}@{1} is not installed")] - PeerNotInstalled(String, Version), - - /// An error that occurred while cloning a wally index - #[cfg(feature = "wally")] - #[error("error cloning wally index")] - CloneWallyIndex(#[from] crate::dependencies::wally::CloneWallyIndexError), - - /// An error that occurred while parsing a URL - #[error("error parsing URL")] - UrlParse(#[from] url::ParseError), -} - -fn get_by_maybe_url<'a>(indices: &'a Indices, maybe_url: &'a str) -> &'a dyn Index { - if let Ok(url) = maybe_url.parse() { - get_index_by_url(indices, &url) - } else { - get_index(indices, Some(maybe_url)) - } -} - -impl Manifest { - fn missing_dependencies( - &self, - root: &mut RootLockfileNode, - locked: bool, - project: &Project, - ) -> Result, ResolveError> { - Ok(if let Some(old_root) = project.lockfile()? { - if self.name != old_root.name && locked { - return Err(ResolveError::OutOfDateLockfile); - } - - if self.overrides != old_root.overrides { - // TODO: resolve only the changed dependencies (will this be worth it?) - debug!("overrides have changed, resolving all dependencies"); - return Ok(self.dependencies()); - } - - debug!("lockfile found, resolving dependencies from it"); - let mut missing = BTreeMap::new(); - - let current_dependencies = self.dependencies(); - let current_specifiers = current_dependencies - .clone() - .into_iter() - .map(|(desired_name, (specifier, _))| (specifier, desired_name)) - .collect::>(); - - // populate the new lockfile with all root dependencies (and their dependencies) from the old lockfile - for (name, versions) in &old_root.children { - for (version, resolved_package) in versions { - let Some((old_specifier, desired_name)) = old_root - .root_specifier(resolved_package) - .and_then(|(old_specifier, _)| { - current_specifiers - .get(old_specifier) - .map(|desired_name| (old_specifier, desired_name)) - }) - else { - continue; - }; - - root.specifiers.entry(name.clone()).or_default().insert( - version.clone(), - (old_specifier.clone(), desired_name.clone()), - ); - - let mut queue = VecDeque::from([(resolved_package, 0usize)]); - - while let Some((resolved_package, depth)) = queue.pop_front() { - debug!( - "{}resolved {resolved_package} from lockfile", - "\t".repeat(depth) - ); - - root.children - .entry(resolved_package.pkg_ref.name()) - .or_default() - .insert( - resolved_package.pkg_ref.version().clone(), - resolved_package.clone(), - ); - - for (dep_name, (dep_version, _)) in &resolved_package.dependencies { - if root - .children - .get(dep_name) - .and_then(|v| v.get(dep_version)) - .is_some() - { - continue; - } - - let Some(dep) = old_root - .children - .get(dep_name) - .and_then(|v| v.get(dep_version)) - else { - return Err(ResolveError::OutOfDateLockfile); - }; - - queue.push_back((dep, depth + 1)); - } - } - } - } - - let old_specifiers = old_root - .specifiers - .values() - .flat_map(|v| v.values()) - .map(|(specifier, _)| specifier) - .collect::>(); - - // resolve new, or modified, dependencies from the manifest - for (desired_name, (specifier, dep_type)) in current_dependencies { - if old_specifiers.contains(&specifier) { - continue; - } - - if locked { - return Err(ResolveError::OutOfDateLockfile); - } - - missing.insert(desired_name, (specifier.clone(), dep_type)); - } - - debug!( - "resolved {} dependencies from lockfile. new dependencies: {}", - old_root.children.len(), - missing.len() - ); - - missing - } else { - debug!("no lockfile found, resolving all dependencies"); - self.dependencies() - }) - } - - /// Resolves the dependency graph for the project - pub fn dependency_graph( - &self, - project: &mut Project, - locked: bool, - ) -> Result { - debug!("resolving dependency graph for project {}", self.name); - // try to reuse versions (according to semver specifiers) to decrease the amount of downloads and storage - let mut root = RootLockfileNode { - name: self.name.clone(), - overrides: self.overrides.clone(), - specifiers: Default::default(), - children: Default::default(), - }; - - let missing_dependencies = self.missing_dependencies(&mut root, locked, project)?; - - if missing_dependencies.is_empty() { - debug!("no dependencies left to resolve, finishing..."); - return Ok(root); - } - - let overrides = self - .overrides - .iter() - .flat_map(|(k, spec)| k.0.iter().map(|path| (path, spec.clone()))) - .collect::>(); - - debug!("resolving {} dependencies", missing_dependencies.len()); - - let mut queue = missing_dependencies - .into_iter() - .map(|(desired_name, (specifier, dep_type))| { - (desired_name, specifier, dep_type, None, vec![]) - }) - .collect::>(); - - while let Some((desired_name, specifier, dep_type, dependant, mut path)) = queue.pop_front() - { - let depth = path.len(); - - let (pkg_ref, default_realm, dependencies) = match &specifier { - DependencySpecifier::Registry(registry_dependency) => { - // needed because of overrides, which are expected to use the project's indices rather than URLs - let index = get_by_maybe_url(project.indices(), ®istry_dependency.index); - - let entry = find_version_from_index( - &mut root, - index, - &specifier, - registry_dependency.name.clone().into(), - ®istry_dependency.version, - )?; - - debug!( - "{}resolved registry dependency {} to {}", - "\t".repeat(depth), - registry_dependency.name, - entry.version - ); - - ( - PackageRef::Registry(RegistryPackageRef { - name: registry_dependency.name.clone(), - version: entry.version, - index_url: index.url().clone(), - }), - entry.realm, - entry.dependencies, - ) - } - DependencySpecifier::Git(git_dependency) => { - let (manifest, url, rev) = - git_dependency.resolve(project.cache_dir(), project.indices())?; - - debug!( - "{}resolved git dependency {} to {url}#{rev}", - "\t".repeat(depth), - git_dependency.repo - ); - - ( - PackageRef::Git(GitPackageRef { - name: manifest.name.clone(), - version: manifest.version.clone(), - repo_url: url, - rev, - }), - manifest.realm, - manifest.dependencies(), - ) - } - #[cfg(feature = "wally")] - DependencySpecifier::Wally(wally_dependency) => { - let cache_dir = project.cache_dir().to_path_buf(); - let index = crate::dependencies::wally::clone_wally_index( - &cache_dir, - project.indices_mut(), - &wally_dependency.index_url, - )?; - - let entry = find_version_from_index( - &mut root, - &index, - &specifier, - wally_dependency.name.clone().into(), - &wally_dependency.version, - )?; - - debug!( - "{}resolved wally dependency {} to {}", - "\t".repeat(depth), - wally_dependency.name, - entry.version - ); - - ( - PackageRef::Wally(crate::dependencies::wally::WallyPackageRef { - name: wally_dependency.name.clone(), - version: entry.version, - index_url: index.url().clone(), - }), - entry.realm, - entry.dependencies, - ) - } - }; - - // if the dependency is a root dependency, it can be thought of as a normal dependency - let dep_type = if dependant.is_some() { - dep_type - } else { - DependencyType::Normal - }; - - let specifier_realm = specifier.realm().copied(); - - if let Some((dependant_name, dependant_version)) = dependant { - root.children - .get_mut(&dependant_name) - .and_then(|v| v.get_mut(&dependant_version)) - .unwrap() - .dependencies - .insert( - pkg_ref.name(), - (pkg_ref.version().clone(), desired_name.clone()), - ); - } else { - root.specifiers - .entry(pkg_ref.name()) - .or_default() - .insert(pkg_ref.version().clone(), (specifier, desired_name.clone())); - } - - let resolved_versions = root.children.entry(pkg_ref.name()).or_default(); - - if let Some(previously_resolved) = resolved_versions.get_mut(pkg_ref.version()) { - match (&pkg_ref, &previously_resolved.pkg_ref) { - (PackageRef::Registry(r), PackageRef::Git(_g)) => { - return Err(ResolveError::RegistryConflict( - r.name.to_string(), - r.version.clone(), - )); - } - (PackageRef::Git(g), PackageRef::Registry(_r)) => { - return Err(ResolveError::GitConflict( - g.name.to_string(), - g.version.clone(), - )); - } - _ => (), - } - - if previously_resolved.dep_type == DependencyType::Peer - && dep_type == DependencyType::Normal - { - previously_resolved.dep_type = dep_type; - } - - // need not resolve the package again - continue; - } - - if specifier_realm.is_some_and(|realm| realm == Realm::Shared) - && default_realm.is_some_and(|realm| realm == Realm::Server) - { - return Err(ResolveError::IncompatibleRealms( - pkg_ref.name().to_string(), - default_realm.unwrap(), - specifier_realm.unwrap(), - )); - } - - resolved_versions.insert( - pkg_ref.version().clone(), - ResolvedPackage { - pkg_ref: pkg_ref.clone(), - dependencies: Default::default(), - realm: specifier_realm - .unwrap_or_default() - .or(default_realm.unwrap_or_default()), - dep_type, - }, - ); - - path.push(desired_name); - - for (desired_name, (specifier, ty)) in dependencies { - let overridden = overrides.iter().find_map(|(k_path, spec)| { - (path == k_path[..k_path.len() - 1] && k_path.last() == Some(&desired_name)) - .then_some(spec) - }); - - queue.push_back(( - desired_name, - overridden.cloned().unwrap_or(specifier), - ty, - Some((pkg_ref.name(), pkg_ref.version().clone())), - path.clone(), - )); - } - } - - debug!("resolving realms and peer dependencies..."); - - for (name, versions) in root.children.clone() { - for (version, resolved_package) in versions { - if resolved_package.dep_type == DependencyType::Peer { - return Err(ResolveError::PeerNotInstalled( - resolved_package.pkg_ref.name().to_string(), - resolved_package.pkg_ref.version().clone(), - )); - } - - let mut realm = resolved_package.realm; - - for (dep_name, (dep_version, _)) in &resolved_package.dependencies { - let dep = root.children.get(dep_name).and_then(|v| v.get(dep_version)); - - if let Some(dep) = dep { - realm = find_realm(&realm, &dep.realm); - } - } - - root.children - .get_mut(&name) - .and_then(|v| v.get_mut(&version)) - .unwrap() - .realm = realm; - } - } - - debug!("finished resolving dependency graph"); - - Ok(root) - } -} diff --git a/src/dependencies/wally.rs b/src/dependencies/wally.rs deleted file mode 100644 index 3b1a0f7..0000000 --- a/src/dependencies/wally.rs +++ /dev/null @@ -1,365 +0,0 @@ -use std::{ - collections::BTreeMap, - fs::{create_dir_all, read}, - hash::{DefaultHasher, Hash, Hasher}, - io::Cursor, - path::Path, -}; - -use git2::build::RepoBuilder; -use log::{debug, error}; -use semver::{Version, VersionReq}; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use url::Url; - -use crate::{ - dependencies::{maybe_authenticated_request, DependencySpecifier}, - index::{remote_callbacks, IndexFileEntry, WallyIndex}, - manifest::{DependencyType, ManifestConvertError, Realm}, - package_name::{ - FromStrPackageNameParseError, WallyPackageName, WallyPackageNameValidationError, - }, - project::{get_wally_index, Indices}, -}; - -/// A dependency of a package that can be downloaded from a registry -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct WallyDependencySpecifier { - /// The name of the package - #[serde(rename = "wally")] - pub name: WallyPackageName, - /// The version requirement of the package - pub version: VersionReq, - /// The url of the index - pub index_url: Url, - /// The realm of the package - #[serde(skip_serializing_if = "Option::is_none")] - pub realm: Option, -} - -/// A reference to a package that can be downloaded from a registry -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash)] -#[serde(deny_unknown_fields)] -pub struct WallyPackageRef { - /// The name of the package - pub name: WallyPackageName, - /// The version of the package - pub version: Version, - /// The index URL of the package - pub index_url: Url, -} - -/// An error that occurred while downloading a package from a wally registry -#[derive(Debug, Error)] -pub enum WallyDownloadError { - /// An error that occurred while interacting with reqwest - #[error("error interacting with reqwest")] - Reqwest(#[from] reqwest::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// The package was not found on the registry - #[error("package {0} not found on the registry, but found in the index")] - NotFound(WallyPackageName), - - /// The user is unauthorized to download the package - #[error("unauthorized to download package {0}")] - Unauthorized(WallyPackageName), - - /// An HTTP error occurred - #[error("http error {0}: the server responded with {1}")] - Http(reqwest::StatusCode, String), - - /// An error occurred while extracting the archive - #[error("error extracting archive")] - Zip(#[from] zip::result::ZipError), - - /// An error occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error occurred while interacting with serde - #[error("error interacting with serde")] - Serde(#[from] serde_json::Error), - - /// An error occurred while parsing the api URL - #[error("error parsing URL")] - Url(#[from] url::ParseError), - - /// An error occurred while refreshing the index - #[error("error refreshing index")] - RefreshIndex(#[from] crate::index::RefreshError), - - /// An error occurred while converting the manifest - #[error("error converting manifest")] - Manifest(#[from] ManifestConvertError), -} - -/// An error that occurred while cloning a wally index -#[derive(Error, Debug)] -pub enum CloneWallyIndexError { - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while refreshing the index - #[error("error refreshing index")] - RefreshIndex(#[from] crate::index::RefreshError), -} - -pub(crate) fn clone_wally_index( - cache_dir: &Path, - indices: &mut Indices, - index_url: &Url, -) -> Result { - let mut hasher = DefaultHasher::new(); - index_url.hash(&mut hasher); - let url_hash = hasher.finish().to_string(); - - let index_path = cache_dir.join("wally_indices").join(url_hash); - - if index_path.exists() { - debug!("wally index already exists at {}", index_path.display()); - - return Ok(get_wally_index(indices, index_url, Some(&index_path))?.clone()); - } - - debug!( - "cloning wally index from {} to {}", - index_url, - index_path.display() - ); - - create_dir_all(&index_path)?; - - let mut fetch_options = git2::FetchOptions::new(); - fetch_options.remote_callbacks(remote_callbacks!(get_wally_index( - indices, - index_url, - Some(&index_path) - )?)); - - RepoBuilder::new() - .fetch_options(fetch_options) - .clone(index_url.as_ref(), &index_path)?; - - Ok(get_wally_index(indices, index_url, Some(&index_path))?.clone()) -} - -/// The configuration of a wally index -#[derive(Serialize, Deserialize, Debug)] -struct WallyIndexConfig { - /// The URL of the wally API - api: String, -} - -/// An error that occurred while resolving the URL of a wally package -#[derive(Error, Debug)] -pub enum ResolveWallyUrlError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while interacting with the index - #[error("error interacting with the index")] - Index(#[from] crate::index::ConfigError), - - /// An error that occurred while parsing the URL - #[error("error parsing URL")] - Url(#[from] url::ParseError), - - /// An error that occurred while cloning the index - #[error("error cloning index")] - CloneIndex(#[from] CloneWallyIndexError), - - /// An error that occurred while reading the index config - #[error("error reading index config")] - ReadConfig(#[from] serde_json::Error), -} - -fn read_api_url(index_path: &Path) -> Result { - let config_path = index_path.join("config.json"); - let raw_config_contents = read(config_path)?; - let config: WallyIndexConfig = serde_json::from_slice(&raw_config_contents)?; - - Ok(config.api) -} - -impl WallyPackageRef { - /// Resolves the download URL of the package - pub fn resolve_url( - &self, - cache_dir: &Path, - indices: &mut Indices, - ) -> Result { - let index = clone_wally_index(cache_dir, indices, &self.index_url)?; - - let api_url = Url::parse(&read_api_url(&index.path)?)?; - - let url = format!( - "{}/v1/package-contents/{}/{}/{}", - api_url.to_string().trim_end_matches('/'), - self.name.scope(), - self.name.name(), - self.version - ); - - Ok(Url::parse(&url)?) - } - - /// Downloads the package to the specified destination - pub fn download>( - &self, - reqwest_client: &reqwest::blocking::Client, - url: &Url, - registry_auth_token: Option, - dest: P, - ) -> Result<(), WallyDownloadError> { - let response = - maybe_authenticated_request(reqwest_client, url.as_str(), registry_auth_token) - .header( - "Wally-Version", - std::env::var("WALLY_VERSION").unwrap_or("0.3.2".to_string()), - ) - .send()?; - - if !response.status().is_success() { - return match response.status() { - reqwest::StatusCode::NOT_FOUND => { - Err(WallyDownloadError::NotFound(self.name.clone())) - } - reqwest::StatusCode::UNAUTHORIZED => { - Err(WallyDownloadError::Unauthorized(self.name.clone())) - } - _ => Err(WallyDownloadError::Http( - response.status(), - response.text()?, - )), - }; - } - - let bytes = response.bytes()?; - - let mut archive = zip::read::ZipArchive::new(Cursor::new(bytes))?; - archive.extract(dest.as_ref())?; - - Ok(()) - } -} - -#[derive(Deserialize, Clone, Debug)] -#[serde(rename_all = "kebab-case")] -pub(crate) struct WallyPackage { - pub(crate) name: WallyPackageName, - pub(crate) version: Version, - pub(crate) registry: Url, - #[serde(default)] - pub(crate) realm: Option, - #[serde(default)] - pub(crate) description: Option, - #[serde(default)] - pub(crate) license: Option, - #[serde(default)] - pub(crate) authors: Option>, - #[serde(default)] - pub(crate) private: Option, -} - -#[derive(Deserialize, Default, Clone, Debug)] -#[serde(rename_all = "kebab-case")] -pub(crate) struct WallyPlace { - #[serde(default)] - pub(crate) shared_packages: Option, - #[serde(default)] - pub(crate) server_packages: Option, -} - -#[derive(Deserialize, Clone, Debug)] -#[serde(rename_all = "kebab-case")] -pub(crate) struct WallyManifest { - pub(crate) package: WallyPackage, - #[serde(default)] - pub(crate) place: WallyPlace, - #[serde(default)] - pub(crate) dependencies: BTreeMap, - #[serde(default)] - pub(crate) server_dependencies: BTreeMap, - #[serde(default)] - pub(crate) dev_dependencies: BTreeMap, -} - -/// An error that occurred while converting a wally manifest's dependencies -#[derive(Debug, Error)] -pub enum WallyManifestDependencyError { - /// An error that occurred because the dependency specifier is invalid - #[error("invalid dependency specifier: {0}")] - InvalidDependencySpecifier(String), - - /// An error that occurred while parsing a package name - #[error("error parsing package name")] - PackageName(#[from] FromStrPackageNameParseError), - - /// An error that occurred while parsing a version requirement - #[error("error parsing version requirement")] - VersionReq(#[from] semver::Error), -} - -pub(crate) fn parse_wally_dependencies( - manifest: WallyManifest, -) -> Result, WallyManifestDependencyError> { - [ - (manifest.dependencies, Realm::Shared), - (manifest.server_dependencies, Realm::Server), - (manifest.dev_dependencies, Realm::Development), - ] - .into_iter() - .flat_map(|(deps, realm)| { - deps.into_iter() - .map(move |(desired_name, specifier)| (desired_name, specifier, realm)) - .map(|(desired_name, specifier, realm)| { - let (name, req) = specifier.split_once('@').ok_or_else(|| { - WallyManifestDependencyError::InvalidDependencySpecifier(specifier.clone()) - })?; - let name: WallyPackageName = name.parse()?; - let req: VersionReq = req.parse()?; - - Ok(( - desired_name, - DependencySpecifier::Wally(WallyDependencySpecifier { - name, - version: req, - index_url: manifest.package.registry.clone(), - realm: Some(realm), - }), - )) - }) - }) - .collect() -} - -impl TryFrom for IndexFileEntry { - type Error = WallyManifestDependencyError; - - fn try_from(value: WallyManifest) -> Result { - let dependencies = parse_wally_dependencies(value.clone())? - .into_iter() - .map(|(desired_name, specifier)| (desired_name, (specifier, DependencyType::Normal))) - .collect(); - - Ok(IndexFileEntry { - version: value.package.version, - realm: value.package.realm, - published_at: Default::default(), - description: value.package.description, - dependencies, - }) - } -} diff --git a/src/index.rs b/src/index.rs deleted file mode 100644 index 30967e9..0000000 --- a/src/index.rs +++ /dev/null @@ -1,753 +0,0 @@ -use std::{ - any::Any, - collections::{BTreeMap, BTreeSet}, - fmt::Debug, - fs::create_dir_all, - hash::Hash, - path::{Path, PathBuf}, - sync::Arc, -}; - -use chrono::{DateTime, Utc}; -use git2::{build::RepoBuilder, Remote, Repository, Signature}; -use log::debug; -use semver::Version; -use serde::{Deserialize, Serialize}; -use thiserror::Error; -use url::Url; - -use crate::{ - dependencies::DependencySpecifier, - manifest::{DependencyType, Manifest, Realm}, - package_name::PackageName, -}; - -/// Owners of a scope -pub type ScopeOwners = BTreeSet; - -/// A packages index -pub trait Index: Send + Sync + Debug + Any + 'static { - /// Gets the owners of a scope - fn scope_owners(&self, scope: &str) -> Result, ScopeOwnersError>; - - /// Creates a scope - fn create_scope_for( - &mut self, - scope: &str, - owners: &ScopeOwners, - ) -> Result; - - /// Gets a package from the index - fn package(&self, name: &PackageName) -> Result, IndexPackageError>; - - /// Creates a package version - fn create_package_version( - &mut self, - manifest: &Manifest, - uploader: &u64, - ) -> Result, CreatePackageVersionError>; - - /// Gets the index's configuration - fn config(&self) -> Result; - - /// Returns a function that gets the credentials for a git repository - fn credentials_fn(&self) -> Option<&Arc>; - - /// Returns the URL of the index's repository - fn url(&self) -> &Url; - - /// Returns the token to this index's registry - fn registry_auth_token(&self) -> Option<&str> { - None - } - - /// Updates the index - fn refresh(&self) -> Result<(), RefreshError> { - Ok(()) - } - - /// Returns this as Any - fn as_any(&self) -> &dyn Any; -} - -/// A function that gets the credentials for a git repository -pub type CredentialsFn = Box< - dyn Fn() -> Box< - dyn FnMut(&str, Option<&str>, git2::CredentialType) -> Result, - > + Send - + Sync, ->; - -/// The packages index -#[derive(Clone)] -pub struct GitIndex { - path: PathBuf, - repo_url: Url, - registry_auth_token: Option, - pub(crate) credentials_fn: Option>, -} - -impl Debug for GitIndex { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("GitIndex") - .field("path", &self.path) - .field("repo_url", &self.repo_url) - .finish() - } -} - -impl Hash for GitIndex { - fn hash(&self, state: &mut H) { - self.path.hash(state); - self.repo_url.hash(state); - } -} - -impl PartialEq for GitIndex { - fn eq(&self, other: &Self) -> bool { - self.path == other.path && self.repo_url == other.repo_url - } -} - -impl Eq for GitIndex {} - -/// An error that occurred while getting the index's refspec -#[derive(Debug, Error)] -pub enum GetRefSpecError { - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// The refspec for the upstream branch was not found - #[error("refspec not found for upstream branch {0}")] - RefSpecNotFound(String), - - /// The refspec is not utf-8 - #[error("refspec not utf-8")] - RefSpecNotUtf8, - - /// The upstream branch was not found - #[error("upstream branch not found")] - UpstreamBranchNotFound, - - /// The upstream branch is not utf-8 - #[error("upstream branch not utf-8")] - UpstreamBranchNotUtf8, -} - -/// An error that occurred while refreshing the index -#[derive(Debug, Error)] -pub enum RefreshError { - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while getting the index's refspec - #[error("error getting refspec")] - GetRefSpec(#[from] GetRefSpecError), -} - -/// An error that occurred while interacting with the scope owners -#[derive(Debug, Error)] -pub enum ScopeOwnersError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while deserializing the scope owners - #[error("error deserializing scope owners")] - ScopeOwnersDeser(#[source] serde_yaml::Error), - - /// An error that occurred while committing and pushing to the index - #[error("error committing and pushing to the index")] - CommitAndPush(#[from] CommitAndPushError), -} - -/// An error that occurred while committing and pushing to the index -#[derive(Debug, Error)] -pub enum CommitAndPushError { - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while getting the index's refspec - #[error("error getting refspec")] - GetRefSpec(#[from] GetRefSpecError), -} - -/// An error that occurred while getting a package from the index -#[derive(Debug, Error)] -pub enum IndexPackageError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while deserializing the index file - #[error("error deserializing index file")] - FileDeser(#[source] serde_yaml::Error), - - /// An unknown error occurred - #[error("unknown error")] - Other(#[source] Box), -} - -/// An error that occurred while creating a package version -#[derive(Debug, Error)] -pub enum CreatePackageVersionError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while getting a package from the index - #[error("error getting a package from the index")] - IndexPackage(#[from] IndexPackageError), - - /// An error that occurred while serializing the index file - #[error("error serializing index file")] - FileSer(#[source] serde_yaml::Error), - - /// An error that occurred while committing and pushing to the index - #[error("error committing and pushing to the index")] - CommitAndPush(#[from] CommitAndPushError), - - /// An error that occurred while interacting with the scope owners - #[error("error interacting with the scope owners")] - ScopeOwners(#[from] ScopeOwnersError), - - /// The scope is missing ownership - #[error("missing scope ownership")] - MissingScopeOwnership, - - /// An error that occurred while converting a manifest to an index file entry - #[error("error converting manifest to index file entry")] - FromManifestIndexFileEntry(#[from] FromManifestIndexFileEntry), -} - -/// An error that occurred while getting the index's configuration -#[derive(Debug, Error)] -pub enum ConfigError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while deserializing the index config - #[error("error deserializing index config")] - ConfigDeser(#[source] serde_yaml::Error), - - /// The index does not have a config file - #[error("index does not have a config file - this is an issue with the index, please contact the maintainer of the index")] - MissingConfig, -} - -fn get_refspec( - repo: &Repository, - remote: &mut Remote, -) -> Result<(String, String), GetRefSpecError> { - let upstream_branch_buf = repo.branch_upstream_name( - repo.head()? - .name() - .ok_or(GetRefSpecError::UpstreamBranchNotFound)?, - )?; - let upstream_branch = upstream_branch_buf - .as_str() - .ok_or(GetRefSpecError::UpstreamBranchNotUtf8)?; - - let refspec_buf = remote - .refspecs() - .find(|r| r.direction() == git2::Direction::Fetch && r.dst_matches(upstream_branch)) - .ok_or(GetRefSpecError::RefSpecNotFound( - upstream_branch.to_string(), - ))? - .rtransform(upstream_branch)?; - let refspec = refspec_buf - .as_str() - .ok_or(GetRefSpecError::RefSpecNotUtf8)?; - - Ok((refspec.to_string(), upstream_branch.to_string())) -} - -macro_rules! remote_callbacks { - ($index:expr) => {{ - #[allow(unused_imports)] - use crate::index::Index; - let mut remote_callbacks = git2::RemoteCallbacks::new(); - - if let Some(credentials) = &$index.credentials_fn() { - let credentials = std::sync::Arc::clone(credentials); - - remote_callbacks.credentials(move |a, b, c| credentials()(a, b, c)); - } - - remote_callbacks - }}; -} -pub(crate) use remote_callbacks; - -impl GitIndex { - /// Creates a new git index. The `refresh` method must be called before using the index, preferably immediately after creating it. - pub fn new>( - path: P, - repo_url: &Url, - credentials: Option, - registry_auth_token: Option, - ) -> Self { - Self { - path: path.as_ref().to_path_buf(), - repo_url: repo_url.clone(), - credentials_fn: credentials.map(Arc::new), - registry_auth_token, - } - } - - /// Gets the path of the index - pub fn path(&self) -> &Path { - &self.path - } - - /// Commits and pushes to the index - pub fn commit_and_push( - &self, - message: &str, - signature: &Signature, - ) -> Result<(), CommitAndPushError> { - let repo = Repository::open(&self.path)?; - - let mut index = repo.index()?; - index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?; - index.write()?; - - let oid = index.write_tree()?; - let tree = repo.find_tree(oid)?; - - let parent_commit = repo.head()?.peel_to_commit()?; - - repo.commit( - Some("HEAD"), - signature, - signature, - message, - &tree, - &[&parent_commit], - )?; - - let mut remote = repo.find_remote("origin")?; - - let (refspec, _) = get_refspec(&repo, &mut remote)?; - - remote.push( - &[&refspec], - Some(git2::PushOptions::new().remote_callbacks(remote_callbacks!(self))), - )?; - - Ok(()) - } -} - -macro_rules! refresh_git_based_index { - ($index:expr) => {{ - let repo = if $index.path.exists() { - Repository::open(&$index.path).ok() - } else { - None - }; - - if let Some(repo) = repo { - let mut remote = repo.find_remote("origin")?; - let (refspec, upstream_branch) = get_refspec(&repo, &mut remote)?; - - remote.fetch( - &[&refspec], - Some(git2::FetchOptions::new().remote_callbacks(remote_callbacks!($index))), - None, - )?; - - let commit = repo.find_reference(&upstream_branch)?.peel_to_commit()?; - - debug!( - "refreshing index, fetching {refspec}#{} from origin", - commit.id().to_string() - ); - - repo.reset(&commit.into_object(), git2::ResetType::Hard, None)?; - - Ok(()) - } else { - debug!( - "refreshing index - first time, cloning {} into {}", - $index.repo_url, - $index.path.display() - ); - create_dir_all(&$index.path)?; - - let mut fetch_options = git2::FetchOptions::new(); - fetch_options.remote_callbacks(remote_callbacks!($index)); - - RepoBuilder::new() - .fetch_options(fetch_options) - .clone(&$index.repo_url.to_string(), &$index.path)?; - - Ok(()) - } - }}; -} - -impl Index for GitIndex { - fn scope_owners(&self, scope: &str) -> Result, ScopeOwnersError> { - let path = self.path.join(scope).join("owners.yaml"); - - if !path.exists() { - return Ok(None); - } - - let contents = std::fs::read(&path)?; - let owners: ScopeOwners = - serde_yaml::from_slice(&contents).map_err(ScopeOwnersError::ScopeOwnersDeser)?; - - Ok(Some(owners)) - } - - fn create_scope_for( - &mut self, - scope: &str, - owners: &ScopeOwners, - ) -> Result { - let path = self.path.join(scope); - - if path.exists() { - return Ok(false); - } - - create_dir_all(&path)?; - - serde_yaml::to_writer(std::fs::File::create(path.join("owners.yaml"))?, owners) - .map_err(ScopeOwnersError::ScopeOwnersDeser)?; - - Ok(true) - } - - fn package(&self, name: &PackageName) -> Result, IndexPackageError> { - let path = self.path.join(name.scope()).join(name.name()); - - if !path.exists() { - return Ok(None); - } - - let contents = std::fs::read(&path)?; - let file: IndexFile = - serde_yaml::from_slice(&contents).map_err(IndexPackageError::FileDeser)?; - - Ok(Some(file)) - } - - fn create_package_version( - &mut self, - manifest: &Manifest, - uploader: &u64, - ) -> Result, CreatePackageVersionError> { - let scope = manifest.name.scope(); - - if let Some(owners) = self.scope_owners(scope)? { - if !owners.contains(uploader) { - return Err(CreatePackageVersionError::MissingScopeOwnership); - } - } else if !self.create_scope_for(scope, &BTreeSet::from([*uploader]))? { - return Err(CreatePackageVersionError::MissingScopeOwnership); - } - - let path = self.path.join(scope); - - let mut file = - if let Some(file) = self.package(&PackageName::Standard(manifest.name.clone()))? { - if file.iter().any(|e| e.version == manifest.version) { - return Ok(None); - } - file - } else { - BTreeSet::new() - }; - - let entry: IndexFileEntry = manifest.clone().try_into()?; - file.insert(entry.clone()); - - serde_yaml::to_writer( - std::fs::File::create(path.join(manifest.name.name()))?, - &file, - ) - .map_err(CreatePackageVersionError::FileSer)?; - - Ok(Some(entry)) - } - - fn config(&self) -> Result { - let path = self.path.join("config.yaml"); - - if !path.exists() { - return Err(ConfigError::MissingConfig); - } - - let contents = std::fs::read(&path)?; - let config: IndexConfig = - serde_yaml::from_slice(&contents).map_err(ConfigError::ConfigDeser)?; - - Ok(config) - } - - fn credentials_fn(&self) -> Option<&Arc> { - self.credentials_fn.as_ref() - } - - fn url(&self) -> &Url { - &self.repo_url - } - - fn registry_auth_token(&self) -> Option<&str> { - self.registry_auth_token.as_deref() - } - - fn refresh(&self) -> Result<(), RefreshError> { - refresh_git_based_index!(self) - } - - fn as_any(&self) -> &dyn Any { - self - } -} - -/// The configuration of the index -#[derive(Serialize, Deserialize, Debug, Clone)] -#[serde(deny_unknown_fields)] -pub struct IndexConfig { - /// The URL of the index's API - pub api: Url, - /// The URL of the index's download API, defaults to `{API_URL}/v0/packages/{PACKAGE_AUTHOR}/{PACKAGE_NAME}/{PACKAGE_VERSION}`. - /// Has the following variables: - /// - `{API_URL}`: The URL of the index's API (without trailing `/`) - /// - `{PACKAGE_AUTHOR}`: The author of the package - /// - `{PACKAGE_NAME}`: The name of the package - /// - `{PACKAGE_VERSION}`: The version of the package - pub download: Option, - /// Whether to allow git dependencies - #[serde(default)] - pub git_allowed: bool, - /// Whether to allow custom registries - #[serde(default)] - pub custom_registry_allowed: bool, - /// The OAuth client ID for GitHub OAuth - pub github_oauth_client_id: String, -} - -impl IndexConfig { - /// Gets the URL of the index's API - pub fn api(&self) -> &str { - self.api.as_str().trim_end_matches('/') - } - - /// Gets the URL of the index's download API - pub fn download(&self) -> String { - self.download - .as_ref() - .unwrap_or( - &"{API_URL}/v0/packages/{PACKAGE_AUTHOR}/{PACKAGE_NAME}/{PACKAGE_VERSION}" - .to_string(), - ) - .replace("{API_URL}", self.api()) - } -} - -/// An entry in the index file -#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] -pub struct IndexFileEntry { - /// The version of the package - pub version: Version, - /// The realm of the package - pub realm: Option, - /// When the package was published - #[serde(default = "Utc::now")] - pub published_at: DateTime, - - /// A description of the package - #[serde(default, skip_serializing_if = "Option::is_none")] - pub description: Option, - - /// The dependencies of the package - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub dependencies: BTreeMap, -} - -/// An error that occurred while converting a manifest to an index file entry -#[derive(Debug, Error)] -pub enum FromManifestIndexFileEntry { - /// An error that occurred because an index is not specified - #[error("index {0} is not specified")] - IndexNotSpecified(String), -} - -impl TryFrom for IndexFileEntry { - type Error = FromManifestIndexFileEntry; - - fn try_from(manifest: Manifest) -> Result { - let dependencies = manifest.dependencies(); - let indices = manifest.indices; - - Ok(Self { - version: manifest.version, - realm: manifest.realm, - published_at: Utc::now(), - - description: manifest.description, - - dependencies: dependencies - .into_iter() - .map(|(desired_name, (dep, ty))| { - Ok(( - desired_name, - match dep { - DependencySpecifier::Registry(mut registry) => { - registry.index = indices - .get(®istry.index) - .ok_or_else(|| { - FromManifestIndexFileEntry::IndexNotSpecified( - registry.index.clone(), - ) - })? - .clone(); - (DependencySpecifier::Registry(registry), ty) - } - d => (d, ty), - }, - )) - }) - .collect::>()?, - }) - } -} - -impl PartialOrd for IndexFileEntry { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.version.cmp(&other.version)) - } -} - -impl Ord for IndexFileEntry { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.version.cmp(&other.version) - } -} - -/// An index file -pub type IndexFile = BTreeSet; - -#[cfg(feature = "wally")] -#[derive(Clone)] -pub(crate) struct WallyIndex { - repo_url: Url, - registry_auth_token: Option, - credentials_fn: Option>, - pub(crate) path: PathBuf, -} - -#[cfg(feature = "wally")] -impl Debug for WallyIndex { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("WallyIndex") - .field("path", &self.path) - .field("repo_url", &self.repo_url) - .finish() - } -} - -#[cfg(feature = "wally")] -impl WallyIndex { - pub(crate) fn new( - repo_url: Url, - registry_auth_token: Option, - path: &Path, - credentials_fn: Option>, - ) -> Self { - Self { - repo_url, - registry_auth_token, - path: path.to_path_buf(), - credentials_fn, - } - } -} - -#[cfg(feature = "wally")] -impl Index for WallyIndex { - fn scope_owners(&self, _scope: &str) -> Result, ScopeOwnersError> { - unimplemented!("wally index is a virtual index meant for wally compatibility only") - } - - fn create_scope_for( - &mut self, - _scope: &str, - _owners: &ScopeOwners, - ) -> Result { - unimplemented!("wally index is a virtual index meant for wally compatibility only") - } - - fn package(&self, name: &PackageName) -> Result, IndexPackageError> { - let path = self.path.join(name.scope()).join(name.name()); - - if !path.exists() { - return Ok(None); - } - - let file = std::fs::File::open(&path)?; - let file = std::io::BufReader::new(file); - - let manifest_stream = serde_json::Deserializer::from_reader(file) - .into_iter::() - .collect::, _>>() - .map_err(|e| IndexPackageError::Other(Box::new(e)))?; - - Ok(Some( - manifest_stream - .into_iter() - .map(|m| m.try_into()) - .collect::, _>>() - .map_err(|e| IndexPackageError::Other(Box::new(e)))?, - )) - } - - fn create_package_version( - &mut self, - _manifest: &Manifest, - _uploader: &u64, - ) -> Result, CreatePackageVersionError> { - unimplemented!("wally index is a virtual index meant for wally compatibility only") - } - - fn config(&self) -> Result { - unimplemented!("wally index is a virtual index meant for wally compatibility only") - } - - fn credentials_fn(&self) -> Option<&Arc> { - self.credentials_fn.as_ref() - } - - fn url(&self) -> &Url { - &self.repo_url - } - - fn registry_auth_token(&self) -> Option<&str> { - self.registry_auth_token.as_deref() - } - - fn refresh(&self) -> Result<(), RefreshError> { - refresh_git_based_index!(self) - } - - fn as_any(&self) -> &dyn Any { - self - } -} diff --git a/src/lib.rs b/src/lib.rs index 9db2de4..4875efd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,51 +1,160 @@ -#![deny(missing_docs)] -//! pesde is a package manager for Roblox that is designed to be feature-rich and easy to use. -//! Currently, pesde is in a very early stage of development, but already supports the following features: -//! - Managing dependencies -//! - Re-exporting types -//! - `bin` exports (ran with Lune) -//! - Patching packages -//! - Downloading packages from Wally registries +// #![deny(missing_docs)] - TODO: bring this back before publishing 0.5 -/// Resolving, downloading and managing dependencies -pub mod dependencies; -/// Managing the pesde index -pub mod index; -/// Creating linking files ('re-export' modules) -pub mod linking_file; -/// Managing the pesde manifest +#[cfg(not(any(feature = "roblox", feature = "lune", feature = "luau")))] +compile_error!("at least one of the features `roblox`, `lune`, or `luau` must be enabled"); + +use once_cell::sync::Lazy; +use std::path::{Path, PathBuf}; + +pub mod lockfile; pub mod manifest; -/// Multi-threading utilities -pub mod multithread; -/// Creating, parsing, and validating package names -pub mod package_name; -/// Managing patches -pub mod patches; -/// Managing pesde projects -pub mod project; +pub mod names; +pub mod source; -/// The folder that contains shared packages -pub const PACKAGES_FOLDER: &str = "packages"; -/// The folder that contains dev packages -pub const DEV_PACKAGES_FOLDER: &str = "dev_packages"; -/// The folder that contains server packages -pub const SERVER_PACKAGES_FOLDER: &str = "server_packages"; -/// The folder that contains the packages index (where every package is stored after being downloaded) -pub const INDEX_FOLDER: &str = "pesde_index"; -/// The name of the manifest file pub const MANIFEST_FILE_NAME: &str = "pesde.yaml"; -/// The name of the lockfile -pub const LOCKFILE_FILE_NAME: &str = "pesde-lock.yaml"; -/// The name of the patches folder -pub const PATCHES_FOLDER: &str = "patches"; -/// Files to be ignored when publishing -pub const IGNORED_FOLDERS: &[&str] = &[ - PACKAGES_FOLDER, - DEV_PACKAGES_FOLDER, - SERVER_PACKAGES_FOLDER, - ".git", -]; +pub const LOCKFILE_FILE_NAME: &str = "pesde.lock"; -pub(crate) fn is_default(t: &T) -> bool { - t == &Default::default() +pub(crate) static REQWEST_CLIENT: Lazy = Lazy::new(|| { + reqwest::blocking::Client::builder() + .user_agent(concat!( + env!("CARGO_PKG_NAME"), + "/", + env!("CARGO_PKG_VERSION") + )) + .build() + .expect("failed to create reqwest client") +}); + +#[derive(Debug, Clone)] +pub struct GitAccount { + username: String, + password: secrecy::SecretString, +} + +impl GitAccount { + pub fn new>(username: String, password: S) -> Self { + GitAccount { + username, + password: password.into(), + } + } + + pub fn as_account(&self) -> gix::sec::identity::Account { + use secrecy::ExposeSecret; + + gix::sec::identity::Account { + username: self.username.clone(), + password: self.password.expose_secret().to_string(), + } + } +} + +impl From for GitAccount { + fn from(account: gix::sec::identity::Account) -> Self { + GitAccount { + username: account.username, + password: account.password.into(), + } + } +} + +#[derive(Debug, Default, Clone)] +pub struct AuthConfig { + pesde_token: Option, + git_credentials: Option, +} + +impl AuthConfig { + pub fn new() -> Self { + AuthConfig::default() + } + + pub fn with_pesde_token>(mut self, token: Option) -> Self { + self.pesde_token = token.map(Into::into); + self + } + + pub fn with_git_credentials(mut self, git_credentials: Option) -> Self { + self.git_credentials = git_credentials; + self + } +} + +pub(crate) fn authenticate_conn( + conn: &mut gix::remote::Connection< + '_, + '_, + Box, + >, + auth_config: AuthConfig, +) { + if let Some(iden) = auth_config.git_credentials { + conn.set_credentials(move |action| match action { + gix::credentials::helper::Action::Get(ctx) => { + Ok(Some(gix::credentials::protocol::Outcome { + identity: iden.as_account(), + next: gix::credentials::helper::NextAction::from(ctx), + })) + } + gix::credentials::helper::Action::Store(_) => Ok(None), + gix::credentials::helper::Action::Erase(_) => Ok(None), + }); + } +} + +#[derive(Debug)] +pub struct Project { + path: PathBuf, + data_dir: PathBuf, + auth_config: AuthConfig, +} + +impl Project { + pub fn new, Q: AsRef>( + path: P, + data_dir: Q, + auth_config: AuthConfig, + ) -> Self { + Project { + path: path.as_ref().to_path_buf(), + data_dir: data_dir.as_ref().to_path_buf(), + auth_config, + } + } + + pub fn path(&self) -> &Path { + &self.path + } + + pub fn data_dir(&self) -> &Path { + &self.data_dir + } + + pub fn read_manifest(&self) -> Result, errors::ManifestReadError> { + let bytes = std::fs::read(self.path.join(MANIFEST_FILE_NAME))?; + Ok(bytes) + } + + pub fn deser_manifest(&self) -> Result { + let bytes = std::fs::read(self.path.join(MANIFEST_FILE_NAME))?; + Ok(serde_yaml::from_slice(&bytes)?) + } + + pub fn write_manifest>(&self, manifest: S) -> Result<(), std::io::Error> { + std::fs::write(self.path.join(MANIFEST_FILE_NAME), manifest.as_ref()) + } +} + +pub mod errors { + use thiserror::Error; + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum ManifestReadError { + #[error("io error reading manifest file")] + Io(#[from] std::io::Error), + + #[error("error deserializing manifest file")] + Serde(#[from] serde_yaml::Error), + } } diff --git a/src/linking_file.rs b/src/linking_file.rs deleted file mode 100644 index dd6b330..0000000 --- a/src/linking_file.rs +++ /dev/null @@ -1,317 +0,0 @@ -use std::{ - collections::HashSet, - fs::{create_dir_all, read_to_string, write}, - path::{Component, Path, PathBuf}, -}; - -use full_moon::{ - ast::types::ExportedTypeDeclaration, - parse, - visitors::{Visit, Visitor}, -}; -use log::debug; -use semver::Version; -use thiserror::Error; - -use crate::{ - dependencies::resolution::{packages_folder, ResolvedPackage, RootLockfileNode}, - manifest::{Manifest, ManifestReadError, PathStyle, Realm}, - package_name::PackageName, - project::Project, -}; - -struct TypeVisitor { - pub(crate) types: Vec, -} - -impl Visitor for TypeVisitor { - fn visit_exported_type_declaration(&mut self, node: &ExportedTypeDeclaration) { - let name = node.type_declaration().type_name().to_string(); - - let (declaration_generics, generics) = - if let Some(declaration) = node.type_declaration().generics() { - let mut declaration_generics = vec![]; - let mut generics = vec![]; - - for generic in declaration.generics().iter() { - declaration_generics.push(generic.to_string()); - - if generic.default_type().is_some() { - generics.push(generic.parameter().to_string()) - } else { - generics.push(generic.to_string()) - } - } - - ( - format!("<{}>", declaration_generics.join(", ")), - format!("<{}>", generics.join(", ")), - ) - } else { - ("".to_string(), "".to_string()) - }; - - self.types.push(format!( - "export type {name}{declaration_generics} = module.{name}{generics}\n" - )); - } -} - -/// Generates the contents of a linking file, given the require path, and the contents of the target file -/// The contents will be scanned for type exports, and the linking file will be generated accordingly -pub fn linking_file(content: &str, path: &str) -> Result { - let mut linker = format!("local module = require({path})\n"); - let mut visitor = TypeVisitor { types: vec![] }; - - parse(content)?.nodes().visit(&mut visitor); - - for ty in visitor.types { - linker.push_str(&ty); - } - - linker.push_str("return module"); - - Ok(linker) -} - -#[derive(Debug, Error)] -/// An error that occurred while linking dependencies -pub enum LinkingError { - #[error("error interacting with the file system")] - /// An error that occurred while interacting with the file system - Io(#[from] std::io::Error), - - #[error("failed getting file name from {0}")] - /// An error that occurred while getting a file name - FileNameFail(PathBuf), - - #[error("failed converting file name to string")] - /// An error that occurred while converting a file name to a string - FileNameToStringFail, - - #[error("failed getting relative path from {0} to {1}")] - /// An error that occurred while getting a relative path - RelativePathFail(PathBuf, PathBuf), - - #[error("failed getting path parent of {0}")] - /// An error that occurred while getting a path parent - ParentFail(PathBuf), - - #[error("failed to convert path component to string")] - /// An error that occurred while converting a path component to a string - ComponentToStringFail, - - #[error("failed to get path string")] - /// An error that occurred while getting a path string - PathToStringFail, - - #[error("error encoding utf-8 string")] - /// An error that occurred while converting a byte slice to a string - Utf8(#[from] std::str::Utf8Error), - - #[error("error reading manifest")] - /// An error that occurred while reading the manifest of a package - ManifestRead(#[from] ManifestReadError), - - #[error("missing realm {0} in-game path")] - /// An error that occurred while getting the in-game path for a realm - MissingRealmInGamePath(Realm), - - #[error("library source is not valid Luau")] - /// An error that occurred because the library source is not valid Luau - InvalidLuau(#[from] full_moon::Error), -} - -pub(crate) fn link, Q: AsRef>( - project: &Project, - resolved_pkg: &ResolvedPackage, - lockfile: &RootLockfileNode, - destination_dir: P, - parent_dependency_packages_dir: Q, - desired_name: &str, - as_root: bool, -) -> Result<(), LinkingError> { - let (_, source_dir) = resolved_pkg.directory(project.path()); - let file = Manifest::from_path(&source_dir)?; - - let Some(relative_lib_export) = file.exports.lib else { - return Ok(()); - }; - - let lib_export = relative_lib_export.to_path(&source_dir); - - let path_style = &project.manifest().path_style; - let PathStyle::Roblox { place } = &path_style; - - debug!("linking {resolved_pkg} using `{}` path style", path_style); - - let pkg_name = resolved_pkg.pkg_ref.name(); - let name = pkg_name.name(); - - let destination_dir = match lockfile - .specifiers - .get(&pkg_name) - .and_then(|v| v.get(resolved_pkg.pkg_ref.version())) - { - Some((specifier, _)) if as_root => project.path().join(packages_folder( - specifier.realm().copied().unwrap_or_default(), - )), - _ => destination_dir.as_ref().to_path_buf(), - }; - create_dir_all(&destination_dir)?; - let destination_file = destination_dir.join(desired_name.to_string() + ".lua"); - - let realm_folder = project.path().join(resolved_pkg.packages_folder()); - let in_different_folders = realm_folder != parent_dependency_packages_dir.as_ref(); - - let mut path = if in_different_folders { - pathdiff::diff_paths(&source_dir, &realm_folder) - .ok_or_else(|| LinkingError::RelativePathFail(source_dir.clone(), realm_folder))? - } else { - pathdiff::diff_paths(&source_dir, &destination_dir).ok_or_else(|| { - LinkingError::RelativePathFail(source_dir.clone(), destination_dir.to_path_buf()) - })? - }; - path.set_extension(""); - - let beginning = if in_different_folders { - place - .get(&resolved_pkg.realm) - .ok_or_else(|| LinkingError::MissingRealmInGamePath(resolved_pkg.realm))? - .clone() - } else if name == "init" { - "script".to_string() - } else { - "script.Parent".to_string() - }; - - let mut components = path - .components() - .map(|component| { - Ok(match component { - Component::ParentDir => ".Parent".to_string(), - Component::Normal(part) => format!( - "[{:?}]", - part.to_str().ok_or(LinkingError::ComponentToStringFail)? - ), - _ => unreachable!("invalid path component"), - }) - }) - .collect::, LinkingError>>()?; - components.pop(); - - let path = beginning + &components.join("") + &format!("[{name:?}]"); - - debug!( - "writing linking file for {} with import `{path}` to {}", - source_dir.display(), - destination_file.display() - ); - - let file_contents = match relative_lib_export.as_str() { - "true" => "".to_string(), - _ => read_to_string(lib_export)?, - }; - - let linking_file_contents = linking_file(&file_contents, &path)?; - - write(&destination_file, linking_file_contents)?; - - Ok(()) -} - -#[derive(Debug, Error)] -#[error("error linking {1}@{2} to {3}@{4}")] -/// An error that occurred while linking the dependencies -pub struct LinkingDependenciesError( - #[source] LinkingError, - PackageName, - Version, - PackageName, - Version, -); - -impl Project { - /// Links the dependencies of the project - pub fn link_dependencies( - &self, - lockfile: &RootLockfileNode, - ) -> Result<(), LinkingDependenciesError> { - let root_deps = lockfile - .specifiers - .iter() - .flat_map(|(name, versions)| versions.keys().map(|version| (name.clone(), version))) - .collect::>(); - - for (name, versions) in &lockfile.children { - for (version, resolved_pkg) in versions { - let (container_dir, _) = resolved_pkg.directory(self.path()); - - debug!( - "linking package {name}@{version}'s dependencies to directory {}", - container_dir.display() - ); - - for (dep_name, (dep_version, desired_name)) in &resolved_pkg.dependencies { - let dep = lockfile - .children - .get(dep_name) - .and_then(|versions| versions.get(dep_version)) - .unwrap(); - - link( - self, - dep, - lockfile, - &container_dir, - &self.path().join(resolved_pkg.packages_folder()), - desired_name, - false, - ) - .map_err(|e| { - LinkingDependenciesError( - e, - dep_name.clone(), - dep_version.clone(), - name.clone(), - version.clone(), - ) - })?; - } - - if root_deps.contains(&(name.clone(), version)) { - let (specifier, desired_name) = lockfile.root_specifier(resolved_pkg).unwrap(); - let linking_dir = &self.path().join(packages_folder( - specifier.realm().copied().unwrap_or_default(), - )); - - debug!( - "linking root package {name}@{version} to directory {}", - linking_dir.display() - ); - - link( - self, - resolved_pkg, - lockfile, - linking_dir, - self.path().join(resolved_pkg.packages_folder()), - desired_name, - true, - ) - .map_err(|e| { - LinkingDependenciesError( - e, - name.clone(), - version.clone(), - name.clone(), - version.clone(), - ) - })?; - } - } - } - - Ok(()) - } -} diff --git a/src/lockfile.rs b/src/lockfile.rs new file mode 100644 index 0000000..126867a --- /dev/null +++ b/src/lockfile.rs @@ -0,0 +1,20 @@ +use crate::{ + names::{PackageName, PackageNames}, + source::{DependencySpecifiers, PackageRefs}, +}; +use semver::Version; +use serde::{Deserialize, Serialize}; +use std::collections::BTreeMap; + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct Lockfile { + pub name: PackageName, + + pub specifiers: BTreeMap>, + pub dependencies: BTreeMap>, +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +pub struct LockfileNode { + pub pkg_ref: PackageRefs, +} diff --git a/src/main.rs b/src/main.rs index 48aca38..75253ed 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,17 +1,48 @@ -use once_cell::sync::Lazy; - -use cli::{auth::auth_command, config::config_command, root::root_command}; - -use crate::cli::{CliConfig, Command, CLI, MULTI}; +use crate::cli::get_token; +use clap::Parser; +use colored::Colorize; +use pesde::{AuthConfig, Project}; mod cli; -fn main() -> anyhow::Result<()> { - Lazy::force(&MULTI); +#[derive(Parser, Debug)] +#[clap(version, about = "pesde is a feature-rich package manager for Luau")] +#[command(disable_version_flag = true)] +struct Cli { + /// Print version + #[arg(short = 'v', short_alias = 'V', long, action = clap::builder::ArgAction::Version)] + version: (), - match CLI.command.clone() { - Command::Auth { command } => auth_command(command), - Command::Config { command } => config_command(command), - cmd => root_command(cmd), + #[command(subcommand)] + subcommand: cli::SubCommand, +} + +fn main() { + let project_dirs = + directories::ProjectDirs::from("com", env!("CARGO_PKG_NAME"), env!("CARGO_BIN_NAME")) + .expect("couldn't get home directory"); + let cwd = std::env::current_dir().expect("failed to get current working directory"); + let cli = Cli::parse(); + + let data_dir = project_dirs.data_dir(); + + if let Err(err) = get_token(data_dir).and_then(|token| { + cli.subcommand.run(Project::new( + cwd, + data_dir, + AuthConfig::new().with_pesde_token(token), + )) + }) { + eprintln!("{}: {}\n", "error".red().bold(), err.to_string().bold()); + + let cause = err.chain().skip(1).collect::>(); + + if !cause.is_empty() { + eprintln!("{}:", "caused by".red().bold()); + for err in cause { + eprintln!(" - {}", err.to_string().bold()); + } + } + std::process::exit(1); } } diff --git a/src/manifest.rs b/src/manifest.rs index 8f8f76b..520413a 100644 --- a/src/manifest.rs +++ b/src/manifest.rs @@ -1,415 +1,174 @@ -use std::{collections::BTreeMap, fmt::Display, fs::read, path::Path, str::FromStr}; - -use cfg_if::cfg_if; +use crate::{names::PackageName, source::DependencySpecifiers}; use relative_path::RelativePathBuf; use semver::Version; -use serde::{Deserialize, Serialize}; -use thiserror::Error; - -use crate::{ - dependencies::DependencySpecifier, package_name::StandardPackageName, MANIFEST_FILE_NAME, +use serde::{de::Visitor, Deserialize, Deserializer, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; +use std::{ + collections::BTreeMap, + fmt::{Display, Formatter}, + str::FromStr, }; -/// The files exported by the package #[derive(Serialize, Deserialize, Debug, Clone, Default)] #[serde(deny_unknown_fields)] pub struct Exports { - /// Points to the file which exports the package. As of currently this is only used for re-exporting types. - /// Libraries must have a structure in Roblox where the main file becomes the folder, for example: - /// A package called pesde/lib has a file called src/main.lua. - /// pesde puts this package in a folder called pesde_lib. - /// The package has to have set up configuration for file-syncing tools such as Rojo so that src/main.lua becomes the pesde_lib and turns it into a ModuleScript #[serde(default, skip_serializing_if = "Option::is_none")] pub lib: Option, - /// Points to the file that will be executed with Lune #[serde(default, skip_serializing_if = "Option::is_none")] pub bin: Option, } -/// The path style used by the package -#[derive(Serialize, Deserialize, Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] #[serde(rename_all = "snake_case", deny_unknown_fields)] -pub enum PathStyle { - /// The path style used by Roblox (e.g. `script.Parent` or `script.Parent.Parent`) - Roblox { - /// A map of realm to in-game package folder location (used for linking between packages in different realms) - #[serde(default)] - place: BTreeMap, - }, +pub enum Target { + #[cfg(feature = "roblox")] + Roblox, + #[cfg(feature = "lune")] + Lune, + #[cfg(feature = "luau")] + Luau, } -impl Default for PathStyle { - fn default() -> Self { - PathStyle::Roblox { - place: BTreeMap::new(), - } - } -} - -impl Display for PathStyle { +impl Display for Target { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - PathStyle::Roblox { .. } => write!(f, "roblox"), + #[cfg(feature = "roblox")] + Target::Roblox => write!(f, "roblox"), + #[cfg(feature = "lune")] + Target::Lune => write!(f, "lune"), + #[cfg(feature = "luau")] + Target::Luau => write!(f, "luau"), + } + } +} + +impl Target { + // self is the project's target, dependency is the target of the dependency + fn is_compatible_with(&self, dependency: &Self) -> bool { + if self == dependency { + return true; + } + + match (self, dependency) { + #[cfg(all(feature = "lune", feature = "luau"))] + (Target::Lune, Target::Luau) => true, + + _ => false, } } } -/// The realm of the package #[derive( - Serialize, Deserialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Copy, Default, + Debug, DeserializeFromStr, SerializeDisplay, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, )] -#[serde(rename_all = "snake_case", deny_unknown_fields)] -pub enum Realm { - /// The package is shared (usually ReplicatedStorage) - #[default] - Shared, - /// The package is server only (usually ServerScriptService/ServerStorage) - Server, - /// The package is development only - Development, -} - -impl Realm { - /// Returns the most restrictive realm - pub fn or(self, other: Self) -> Self { - match self { - Realm::Shared => other, - _ => self, - } - } -} - -impl Display for Realm { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Realm::Shared => write!(f, "shared"), - Realm::Server => write!(f, "server"), - Realm::Development => write!(f, "development"), - } - } -} - -/// An error that occurred while parsing a realm from a string -#[derive(Debug, Error)] -#[error("invalid realm {0}")] -pub struct FromStrRealmError(String); - -impl FromStr for Realm { - type Err = FromStrRealmError; - - fn from_str(s: &str) -> Result { - match s { - "shared" => Ok(Realm::Shared), - "server" => Ok(Realm::Server), - "development" => Ok(Realm::Development), - _ => Err(FromStrRealmError(s.to_string())), - } - } -} - -/// A key to override dependencies -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct OverrideKey(pub Vec>); -impl Serialize for OverrideKey { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str( - &self - .0 +impl FromStr for OverrideKey { + type Err = errors::OverrideKeyFromStr; + + fn from_str(s: &str) -> Result { + Ok(Self( + s.split(',') + .map(|overrides| overrides.split('>').map(|s| s.to_string()).collect()) + .collect(), + )) + } +} + +impl Display for OverrideKey { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!( + f, + "{}", + self.0 .iter() .map(|overrides| { overrides .iter() - .map(String::to_string) + .map(|o| o.as_str()) .collect::>() .join(">") }) .collect::>() - .join(","), + .join(",") ) } } -impl<'de> Deserialize<'de> for OverrideKey { - fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - let mut key = Vec::new(); - for overrides in s.split(',') { - key.push( - overrides - .split('>') - .map(|s| String::from_str(s).map_err(serde::de::Error::custom)) - .collect::, _>>()?, - ); +fn deserialize_dep_specs<'de, D>( + deserializer: D, +) -> Result, D::Error> +where + D: Deserializer<'de>, +{ + struct SpecsVisitor; + + impl<'de> Visitor<'de> for SpecsVisitor { + type Value = BTreeMap; + + fn expecting(&self, formatter: &mut Formatter) -> std::fmt::Result { + formatter.write_str("a map of dependency specifiers") } - Ok(OverrideKey(key)) + fn visit_map(self, mut map: A) -> Result + where + A: serde::de::MapAccess<'de>, + { + let mut specs = BTreeMap::new(); + + while let Some((key, mut value)) = map.next_entry::()? { + value.set_alias(key.to_string()); + specs.insert(key, value); + } + + Ok(specs) + } } + + deserializer.deserialize_map(SpecsVisitor) } -/// The manifest of a package #[derive(Serialize, Deserialize, Debug, Clone)] pub struct Manifest { - /// The name of the package - pub name: StandardPackageName, - /// The version of the package. Must be [semver](https://semver.org) compatible. The registry will not accept non-semver versions and the CLI will not handle such packages + pub name: PackageName, pub version: Version, - /// A short description of the package - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub description: Option, - /// The license of the package - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub license: Option, - /// The authors of the package - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub authors: Option>, - /// The repository of the package - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] pub repository: Option, - /// The files exported by the package #[serde(default)] pub exports: Exports, - /// The path style to use for linking modules - #[serde(default)] - pub path_style: PathStyle, - /// Whether the package is private (it should not be published) + pub target: Target, #[serde(default)] pub private: bool, - /// The realm of the package - #[serde(default, skip_serializing_if = "Option::is_none")] - pub realm: Option, - /// Indices of the package - pub indices: BTreeMap, - /// The command to generate a `sourcemap.json` + #[serde(default)] + pub indices: BTreeMap, #[cfg(feature = "wally")] - #[serde(default, skip_serializing_if = "Option::is_none")] + #[serde(default)] + pub wally_indices: BTreeMap, + #[cfg(feature = "wally")] + #[serde(default)] pub sourcemap_generator: Option, - /// Dependency overrides - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub overrides: BTreeMap, + #[serde(default)] + pub overrides: BTreeMap, - /// The dependencies of the package - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub dependencies: BTreeMap, - /// The peer dependencies of the package - #[serde(default, skip_serializing_if = "BTreeMap::is_empty")] - pub peer_dependencies: BTreeMap, + #[serde(default, deserialize_with = "deserialize_dep_specs")] + pub dependencies: BTreeMap, + #[serde(default, deserialize_with = "deserialize_dep_specs")] + pub peer_dependencies: BTreeMap, + #[serde(default, deserialize_with = "deserialize_dep_specs")] + pub dev_dependencies: BTreeMap, } -/// An error that occurred while reading the manifest -#[derive(Debug, Error)] -pub enum ManifestReadError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), +pub mod errors { + use thiserror::Error; - /// An error that occurred while deserializing the manifest - #[error("error deserializing manifest")] - ManifestDeser(#[source] serde_yaml::Error), -} - -cfg_if! { - if #[cfg(feature = "wally")] { - /// An error that occurred while converting the manifest - #[derive(Debug, Error)] - pub enum ManifestConvertError { - /// An error that occurred while reading the manifest - #[error("error reading the manifest")] - ManifestRead(#[from] ManifestReadError), - - /// An error that occurred while converting the manifest - #[error("error converting the manifest")] - ManifestConvert(#[source] toml::de::Error), - - /// The given path does not have a parent - #[error("the path {0} does not have a parent")] - NoParent(std::path::PathBuf), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while writing the manifest - #[error("error writing the manifest")] - ManifestWrite(#[from] crate::manifest::ManifestWriteError), - - /// An error that occurred while parsing the dependencies - #[error("error parsing the dependencies")] - DependencyParse(#[from] crate::dependencies::wally::WallyManifestDependencyError), - } - } else { - /// An error that occurred while converting the manifest - pub type ManifestConvertError = ManifestReadError; - } -} - -/// An error that occurred while writing the manifest -#[derive(Debug, Error)] -pub enum ManifestWriteError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while serializing the manifest - #[error("error serializing manifest")] - ManifestSer(#[from] serde_yaml::Error), -} - -/// The type of dependency -#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Default)] -#[serde(rename_all = "snake_case")] -pub enum DependencyType { - /// A normal dependency - #[default] - Normal, - /// A peer dependency - Peer, -} - -pub(crate) fn update_sync_tool_files(project_path: &Path, name: String) -> std::io::Result<()> { - if let Ok(file) = std::fs::File::open(project_path.join("default.project.json")) { - let mut project: serde_json::Value = serde_json::from_reader(file)?; - - if project["name"].as_str() == Some(&name) { - return Ok(()); - } - - project["name"] = serde_json::Value::String(name); - - serde_json::to_writer_pretty( - std::fs::File::create(project_path.join("default.project.json"))?, - &project, - )?; - } - - Ok(()) -} - -impl Manifest { - /// Reads a manifest from a path (if the path is a directory, it will look for the manifest file inside it, otherwise it will read the file directly) - pub fn from_path>(path: P) -> Result { - let path = path.as_ref(); - let path = if path.file_name() == Some(MANIFEST_FILE_NAME.as_ref()) { - path.to_path_buf() - } else { - path.join(MANIFEST_FILE_NAME) - }; - - let raw_contents = read(path)?; - let manifest = - serde_yaml::from_slice(&raw_contents).map_err(ManifestReadError::ManifestDeser)?; - - Ok(manifest) - } - - /// Tries to read the manifest from the given path, and if it fails, tries converting the `wally.toml` and writes a `pesde.yaml` in the same directory - #[cfg(feature = "wally")] - pub fn from_path_or_convert>( - path: P, - ) -> Result { - let dir_path = if path.as_ref().file_name() == Some(MANIFEST_FILE_NAME.as_ref()) { - path.as_ref() - .parent() - .ok_or_else(|| ManifestConvertError::NoParent(path.as_ref().to_path_buf()))? - .to_path_buf() - } else { - path.as_ref().to_path_buf() - }; - - Self::from_path(path).or_else(|_| { - let toml_path = dir_path.join("wally.toml"); - let toml_contents = std::fs::read_to_string(toml_path)?; - let wally_manifest: crate::dependencies::wally::WallyManifest = - toml::from_str(&toml_contents).map_err(ManifestConvertError::ManifestConvert)?; - - let dependencies = - crate::dependencies::wally::parse_wally_dependencies(wally_manifest.clone())?; - - let mut place = BTreeMap::new(); - - if let Some(shared) = wally_manifest.place.shared_packages { - if !shared.is_empty() { - place.insert(Realm::Shared, shared); - } - } - - if let Some(server) = wally_manifest.place.server_packages { - if !server.is_empty() { - place.insert(Realm::Server, server); - } - } - - let manifest = Self { - name: wally_manifest.package.name.clone().into(), - version: wally_manifest.package.version, - exports: Exports { - lib: Some(RelativePathBuf::from("true")), - bin: None, - }, - path_style: PathStyle::Roblox { place }, - private: wally_manifest.package.private.unwrap_or(false), - realm: wally_manifest.package.realm, - indices: BTreeMap::from([( - crate::project::DEFAULT_INDEX_NAME.to_string(), - "".to_string(), - )]), - sourcemap_generator: None, - overrides: BTreeMap::new(), - - dependencies, - peer_dependencies: Default::default(), - description: wally_manifest.package.description, - license: wally_manifest.package.license, - authors: wally_manifest.package.authors, - repository: None, - }; - - manifest.write(&dir_path)?; - - update_sync_tool_files(&dir_path, wally_manifest.package.name.name().to_string())?; - - Ok(manifest) - }) - } - - /// Same as `from_path`, enable the `wally` feature to add support for converting `wally.toml` to `pesde.yaml` - #[cfg(not(feature = "wally"))] - pub fn from_path_or_convert>( - path: P, - ) -> Result { - Self::from_path(path) - } - - /// Returns all dependencies - pub fn dependencies(&self) -> BTreeMap { - self.dependencies - .iter() - .map(|(desired_name, specifier)| { - ( - desired_name.clone(), - (specifier.clone(), DependencyType::Normal), - ) - }) - .chain( - self.peer_dependencies - .iter() - .map(|(desired_name, specifier)| { - ( - desired_name.clone(), - (specifier.clone(), DependencyType::Peer), - ) - }), - ) - .collect() - } - - /// Writes the manifest to a path - pub fn write>(&self, to: P) -> Result<(), ManifestWriteError> { - let manifest_path = to.as_ref().join(MANIFEST_FILE_NAME); - - serde_yaml::to_writer(std::fs::File::create(manifest_path)?, self)?; - - Ok(()) - } + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum OverrideKeyFromStr {} } diff --git a/src/multithread.rs b/src/multithread.rs deleted file mode 100644 index 1ab75c3..0000000 --- a/src/multithread.rs +++ /dev/null @@ -1,47 +0,0 @@ -use std::sync::mpsc::{Receiver, Sender}; -use threadpool::ThreadPool; - -/// A multithreaded job -pub struct MultithreadedJob { - progress: Receiver>, - pool: ThreadPool, -} - -impl MultithreadedJob { - /// Creates a new multithreaded job - pub fn new() -> (Self, Sender>) { - let (tx, rx) = std::sync::mpsc::channel(); - let pool = ThreadPool::new(6); - - (Self { progress: rx, pool }, tx) - } - - /// Returns the progress of the job - pub fn progress(&self) -> &Receiver> { - &self.progress - } - - /// Waits for the job to finish - pub fn wait(self) -> Result<(), E> { - self.pool.join(); - - for result in self.progress { - result?; - } - - Ok(()) - } - - /// Executes a function on the thread pool - pub fn execute(&self, tx: &Sender>, f: F) - where - F: (FnOnce() -> Result<(), E>) + Send + 'static, - { - let sender = tx.clone(); - - self.pool.execute(move || { - let result = f(); - sender.send(result).unwrap(); - }); - } -} diff --git a/src/names.rs b/src/names.rs new file mode 100644 index 0000000..df2bfa3 --- /dev/null +++ b/src/names.rs @@ -0,0 +1,95 @@ +use std::{fmt::Display, str::FromStr}; + +use serde::{Deserialize, Serialize}; +use serde_with::{DeserializeFromStr, SerializeDisplay}; + +#[derive(Debug)] +pub enum ErrorReason { + Scope, + Name, +} + +impl Display for ErrorReason { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + ErrorReason::Scope => write!(f, "scope"), + ErrorReason::Name => write!(f, "name"), + } + } +} + +#[derive( + Debug, DeserializeFromStr, SerializeDisplay, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, +)] +pub struct PackageName(String, String); + +impl FromStr for PackageName { + type Err = errors::PackageNameError; + + fn from_str(s: &str) -> Result { + let (scope, name) = s + .split_once('/') + .ok_or(Self::Err::InvalidFormat(s.to_string()))?; + + for (reason, part) in [(ErrorReason::Scope, scope), (ErrorReason::Name, name)] { + if part.len() < 3 || part.len() > 32 { + return Err(Self::Err::InvalidLength(reason, part.to_string())); + } + + if part.chars().all(|c| c.is_ascii_digit()) { + return Err(Self::Err::OnlyDigits(reason, part.to_string())); + } + + if part.starts_with('_') || part.ends_with('_') { + return Err(Self::Err::PrePostfixUnderscore(reason, part.to_string())); + } + + if !part.chars().all(|c| c.is_ascii_alphanumeric() || c == '_') { + return Err(Self::Err::InvalidCharacters(reason, part.to_string())); + } + } + + Ok(Self(scope.to_string(), name.to_string())) + } +} + +impl Display for PackageName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}/{}", self.0, self.1) + } +} + +impl PackageName { + pub fn as_str(&self) -> (&str, &str) { + (&self.0, &self.1) + } +} + +#[derive(Debug, Deserialize, Serialize, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub enum PackageNames { + Pesde(PackageName), +} + +pub mod errors { + use thiserror::Error; + + use crate::names::ErrorReason; + + #[derive(Debug, Error)] + pub enum PackageNameError { + #[error("package name `{0}` is not in the format `scope/name`")] + InvalidFormat(String), + + #[error("package {0} `{1}` contains characters outside a-z, 0-9, and _")] + InvalidCharacters(ErrorReason, String), + + #[error("package {0} `{1}` contains only digits")] + OnlyDigits(ErrorReason, String), + + #[error("package {0} `{1}` starts or ends with an underscore")] + PrePostfixUnderscore(ErrorReason, String), + + #[error("package {0} `{1}` is not within 3-32 characters long")] + InvalidLength(ErrorReason, String), + } +} diff --git a/src/package_name.rs b/src/package_name.rs deleted file mode 100644 index 61e2715..0000000 --- a/src/package_name.rs +++ /dev/null @@ -1,423 +0,0 @@ -use std::{ - fmt::Debug, - hash::Hash, - {fmt::Display, str::FromStr}, -}; - -use cfg_if::cfg_if; -use serde::{ - de::{IntoDeserializer, Visitor}, - Deserialize, Serialize, -}; -use thiserror::Error; - -/// A package name -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct StandardPackageName(String, String); - -/// An error that occurred while validating a package name part (scope or name) -#[derive(Debug, Error)] -pub enum StandardPackageNameValidationError { - /// The package name part is empty - #[error("package name part cannot be empty")] - EmptyPart, - /// The package name part contains invalid characters (only lowercase ASCII characters, numbers, and underscores are allowed) - #[error("package name {0} part can only contain lowercase ASCII characters, numbers, and underscores")] - InvalidPart(String), - /// The package name part is too long (it cannot be longer than 24 characters) - #[error("package name {0} part cannot be longer than 24 characters")] - PartTooLong(String), -} - -/// Validates a package name part (scope or name) -pub fn validate_part(part: &str) -> Result<(), StandardPackageNameValidationError> { - if part.is_empty() { - return Err(StandardPackageNameValidationError::EmptyPart); - } - - if !part - .chars() - .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '_') - { - return Err(StandardPackageNameValidationError::InvalidPart( - part.to_string(), - )); - } - - if part.len() > 24 { - return Err(StandardPackageNameValidationError::PartTooLong( - part.to_string(), - )); - } - - Ok(()) -} - -/// A wally package name -#[cfg(feature = "wally")] -#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -pub struct WallyPackageName(String, String); - -/// An error that occurred while validating a wally package name part (scope or name) -#[cfg(feature = "wally")] -#[derive(Debug, Error)] -pub enum WallyPackageNameValidationError { - /// The package name part is empty - #[error("wally package name part cannot be empty")] - EmptyPart, - /// The package name part contains invalid characters (only lowercase ASCII characters, numbers, and dashes are allowed) - #[error("wally package name {0} part can only contain lowercase ASCII characters, numbers, and dashes")] - InvalidPart(String), - /// The package name part is too long (it cannot be longer than 64 characters) - #[error("wally package name {0} part cannot be longer than 64 characters")] - PartTooLong(String), -} - -/// Validates a wally package name part (scope or name) -#[cfg(feature = "wally")] -pub fn validate_wally_part(part: &str) -> Result<(), WallyPackageNameValidationError> { - if part.is_empty() { - return Err(WallyPackageNameValidationError::EmptyPart); - } - - if !part - .chars() - .all(|c| c.is_ascii_lowercase() || c.is_ascii_digit() || c == '-') - { - return Err(WallyPackageNameValidationError::InvalidPart( - part.to_string(), - )); - } - - if part.len() > 64 { - return Err(WallyPackageNameValidationError::PartTooLong( - part.to_string(), - )); - } - - Ok(()) -} - -/// An error that occurred while parsing an escaped package name -#[derive(Debug, Error)] -pub enum EscapedPackageNameError { - /// This package name is missing a prefix - #[error("package name is missing prefix {0}")] - MissingPrefix(String), - - /// This is not a valid escaped package name - #[error("package name {0} is not in the format `scope{ESCAPED_SEPARATOR}name`")] - Invalid(String), - - /// The package name is invalid - #[error(transparent)] - InvalidName(#[from] E), -} - -/// An error that occurred while parsing a package name -#[derive(Debug, Error)] -pub enum FromStrPackageNameParseError { - /// This is not a valid package name - #[error("package name {0} is not in the format `scope{SEPARATOR}name`")] - Invalid(String), - - /// The package name is invalid - #[error(transparent)] - InvalidPart(#[from] E), -} - -const SEPARATOR: char = '/'; -const ESCAPED_SEPARATOR: char = '+'; - -macro_rules! name_impl { - ($Name:ident, $Error:ident, $validate:expr, $prefix:expr) => { - impl $Name { - /// Creates a new package name - pub fn new(scope: &str, name: &str) -> Result { - $validate(scope)?; - $validate(name)?; - - Ok(Self(scope.to_string(), name.to_string())) - } - - /// Parses an escaped package name - pub fn from_escaped(s: &str) -> Result> { - if !s.starts_with($prefix) { - return Err(EscapedPackageNameError::MissingPrefix($prefix.to_string())); - } - - let (scope, name) = &s[$prefix.len()..] - .split_once(ESCAPED_SEPARATOR) - .ok_or_else(|| EscapedPackageNameError::Invalid(s.to_string()))?; - Ok(Self::new(scope, name)?) - } - - /// Gets the scope of the package name - pub fn scope(&self) -> &str { - &self.0 - } - - /// Gets the name of the package name - pub fn name(&self) -> &str { - &self.1 - } - - /// Gets the escaped form (for use in file names, etc.) of the package name - pub fn escaped(&self) -> String { - format!("{}{}{ESCAPED_SEPARATOR}{}", $prefix, self.0, self.1) - } - - /// Gets the parts of the package name - pub fn parts(&self) -> (&str, &str) { - (&self.0, &self.1) - } - - /// Returns the prefix for this package name - pub fn prefix() -> &'static str { - $prefix - } - } - - impl Display for $Name { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}{}{SEPARATOR}{}", $prefix, self.0, self.1) - } - } - - impl FromStr for $Name { - type Err = FromStrPackageNameParseError<$Error>; - - fn from_str(s: &str) -> Result { - let len = if s.starts_with($prefix) { - $prefix.len() - } else { - 0 - }; - - let parts: Vec<&str> = s[len..].split(SEPARATOR).collect(); - if parts.len() != 2 { - return Err(FromStrPackageNameParseError::Invalid(s.to_string())); - } - - Ok($Name::new(parts[0], parts[1])?) - } - } - - impl Serialize for $Name { - fn serialize(&self, serializer: S) -> Result { - serializer.serialize_str(&self.to_string()) - } - } - - impl<'de> Deserialize<'de> for $Name { - fn deserialize>( - deserializer: D, - ) -> Result<$Name, D::Error> { - struct NameVisitor; - - impl<'de> Visitor<'de> for NameVisitor { - type Value = $Name; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - formatter, - "a string in the format `{}scope{SEPARATOR}name`", - $prefix - ) - } - - fn visit_str(self, v: &str) -> Result { - v.parse().map_err(E::custom) - } - } - - deserializer.deserialize_str(NameVisitor) - } - } - }; -} - -/// A package name -#[derive(Serialize, Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] -#[serde(untagged)] -pub enum PackageName { - /// A standard package name - Standard(StandardPackageName), - /// A wally package name - #[cfg(feature = "wally")] - Wally(WallyPackageName), -} - -impl PackageName { - /// Gets the scope of the package name - pub fn scope(&self) -> &str { - match self { - PackageName::Standard(name) => name.scope(), - #[cfg(feature = "wally")] - PackageName::Wally(name) => name.scope(), - } - } - - /// Gets the name of the package name - pub fn name(&self) -> &str { - match self { - PackageName::Standard(name) => name.name(), - #[cfg(feature = "wally")] - PackageName::Wally(name) => name.name(), - } - } - - /// Gets the escaped form (for use in file names, etc.) of the package name - pub fn escaped(&self) -> String { - match self { - PackageName::Standard(name) => name.escaped(), - #[cfg(feature = "wally")] - PackageName::Wally(name) => name.escaped(), - } - } - - /// Gets the parts of the package name - pub fn parts(&self) -> (&str, &str) { - match self { - PackageName::Standard(name) => name.parts(), - #[cfg(feature = "wally")] - PackageName::Wally(name) => name.parts(), - } - } - - /// Returns the prefix for this package name - pub fn prefix(&self) -> &'static str { - match self { - PackageName::Standard(_) => StandardPackageName::prefix(), - #[cfg(feature = "wally")] - PackageName::Wally(_) => WallyPackageName::prefix(), - } - } -} - -impl Display for PackageName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PackageName::Standard(name) => write!(f, "{name}"), - #[cfg(feature = "wally")] - PackageName::Wally(name) => write!(f, "{name}"), - } - } -} - -impl From for PackageName { - fn from(name: StandardPackageName) -> Self { - PackageName::Standard(name) - } -} - -#[cfg(feature = "wally")] -impl From for PackageName { - fn from(name: WallyPackageName) -> Self { - PackageName::Wally(name) - } -} - -#[cfg(feature = "wally")] -impl From for StandardPackageName { - fn from(name: WallyPackageName) -> Self { - let (scope, name) = name.parts(); - - StandardPackageName( - scope[..scope.len().min(24)].replace('-', "_"), - name[..name.len().min(24)].replace('-', "_"), - ) - } -} - -name_impl!( - StandardPackageName, - StandardPackageNameValidationError, - validate_part, - "" -); - -#[cfg(feature = "wally")] -name_impl!( - WallyPackageName, - WallyPackageNameValidationError, - validate_wally_part, - "wally#" -); - -impl<'de> Deserialize<'de> for PackageName { - fn deserialize>(deserializer: D) -> Result { - let s = String::deserialize(deserializer)?; - - cfg_if! { - if #[cfg(feature = "wally")] { - if s.starts_with(WallyPackageName::prefix()) { - return Ok(PackageName::Wally( - WallyPackageName::deserialize(s.into_deserializer())?, - )); - } - } - } - - Ok(PackageName::Standard(StandardPackageName::deserialize( - s.into_deserializer(), - )?)) - } -} - -/// An error that occurred while parsing a package name -#[derive(Debug, Error)] -pub enum FromStrPackageNameError { - /// Error parsing the package name as a standard package name - #[error("error parsing standard package name")] - Standard(#[from] FromStrPackageNameParseError), - - /// Error parsing the package name as a wally package name - #[cfg(feature = "wally")] - #[error("error parsing wally package name")] - Wally(#[from] FromStrPackageNameParseError), -} - -impl FromStr for PackageName { - type Err = FromStrPackageNameError; - - fn from_str(s: &str) -> Result { - cfg_if! { - if #[cfg(feature = "wally")] { - if s.starts_with(WallyPackageName::prefix()) { - return Ok(PackageName::Wally(WallyPackageName::from_str(s)?)); - } - } - } - - Ok(PackageName::Standard(StandardPackageName::from_str(s)?)) - } -} - -/// An error that occurred while parsing an escaped package name -#[derive(Debug, Error)] -pub enum FromEscapedStrPackageNameError { - /// Error parsing the package name as a standard package name - #[error("error parsing standard package name")] - Standard(#[from] EscapedPackageNameError), - - /// Error parsing the package name as a wally package name - #[cfg(feature = "wally")] - #[error("error parsing wally package name")] - Wally(#[from] EscapedPackageNameError), -} - -impl PackageName { - /// Like `from_str`, but for escaped package names - pub fn from_escaped_str(s: &str) -> Result { - cfg_if! { - if #[cfg(feature = "wally")] { - if s.starts_with(WallyPackageName::prefix()) { - return Ok(PackageName::Wally(WallyPackageName::from_escaped(s)?)); - } - } - } - - Ok(PackageName::Standard(StandardPackageName::from_escaped(s)?)) - } -} diff --git a/src/patches.rs b/src/patches.rs deleted file mode 100644 index b4409e9..0000000 --- a/src/patches.rs +++ /dev/null @@ -1,214 +0,0 @@ -use std::{ - fs::{read, read_dir}, - path::{Path, PathBuf}, -}; - -use git2::{ApplyLocation, Diff, DiffFormat, DiffLineType, Repository, Signature}; -use log::debug; -use semver::Version; -use thiserror::Error; - -use crate::{ - dependencies::resolution::RootLockfileNode, - package_name::{FromEscapedStrPackageNameError, PackageName}, - project::Project, - PATCHES_FOLDER, -}; - -fn make_signature<'a>() -> Result, git2::Error> { - Signature::now( - env!("CARGO_PKG_NAME"), - concat!(env!("CARGO_PKG_NAME"), "@localhost"), - ) -} - -/// Sets up a patches repository in the specified directory -pub fn setup_patches_repo>(dir: P) -> Result { - let repo = Repository::init(&dir)?; - - { - let signature = make_signature()?; - let mut index = repo.index()?; - index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?; - index.write()?; - - let oid = index.write_tree()?; - let tree = repo.find_tree(oid)?; - - repo.commit(Some("HEAD"), &signature, &signature, "original", &tree, &[])?; - } - - Ok(repo) -} - -/// An error that occurred while creating patches -#[derive(Debug, Error)] -pub enum CreatePatchError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while getting a file name - #[error("failed to get file name from {0}")] - FileNameFail(PathBuf), - - /// An error that occurred while stripping a prefix - #[error("error stripping prefix {1} from path {2}")] - StripPrefixFail(#[source] std::path::StripPrefixError, PathBuf, PathBuf), -} - -/// Creates a patch for the package in the specified directory -pub fn create_patch>(dir: P) -> Result, CreatePatchError> { - let mut patches = vec![]; - let repo = Repository::open(dir.as_ref())?; - - let original = repo.head()?.peel_to_tree()?; - let diff = repo.diff_tree_to_workdir(Some(&original), None)?; - - diff.print(DiffFormat::Patch, |_delta, _hunk, line| { - match line.origin_value() { - DiffLineType::Context | DiffLineType::Addition | DiffLineType::Deletion => { - let origin = line.origin(); - let mut buffer = vec![0; origin.len_utf8()]; - origin.encode_utf8(&mut buffer); - patches.extend(buffer); - } - _ => {} - } - patches.extend(line.content()); - - true - })?; - - Ok(patches) -} - -/// An error that occurred while applying patches -#[derive(Debug, Error)] -pub enum ApplyPatchesError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while interacting with git - #[error("error interacting with git")] - Git(#[from] git2::Error), - - /// An error that occurred while getting a file name - #[error("failed to get file name from {0}")] - FileNameFail(PathBuf), - - /// An error that occurred while converting a path to a string - #[error("failed to convert path to string")] - ToStringFail, - - /// An error that occurred because a patch name was malformed - #[error("malformed patch name {0}")] - MalformedPatchName(String), - - /// An error that occurred while parsing a package name - #[error("failed to parse package name {0}")] - PackageNameParse(#[from] FromEscapedStrPackageNameError), - - /// An error that occurred while getting a file stem - #[error("failed to get file stem")] - FileStemFail, - - /// An error that occurred while reading a file - #[error("failed to read file")] - ReadFail, - - /// An error that occurred because a package was not found in the dependencies - #[error("package {0} not found in the lockfile")] - PackageNotFound(PackageName), - - /// An error that occurred because a version was not found for a package - #[error("version {0} not found for package {1}")] - VersionNotFound(Version, PackageName), - - /// An error that occurred while parsing a version - #[error("failed to parse version")] - VersionParse(#[from] semver::Error), - - /// An error that occurred while stripping a prefix - #[error("strip prefix error")] - StripPrefixFail(#[from] std::path::StripPrefixError), -} - -impl Project { - /// Applies patches for the project - pub fn apply_patches(&self, lockfile: &RootLockfileNode) -> Result<(), ApplyPatchesError> { - let patches_dir = self.path().join(PATCHES_FOLDER); - if !patches_dir.exists() { - return Ok(()); - } - - for file in read_dir(&patches_dir)? { - let file = file?; - if !file.file_type()?.is_file() { - continue; - } - - let path = file.path(); - - let file_name = path - .file_name() - .ok_or_else(|| ApplyPatchesError::FileNameFail(path.clone()))?; - let file_name = file_name.to_str().ok_or(ApplyPatchesError::ToStringFail)?; - - let (package_name, version) = file_name - .strip_suffix(".patch") - .unwrap_or(file_name) - .split_once('@') - .ok_or_else(|| ApplyPatchesError::MalformedPatchName(file_name.to_string()))?; - - let package_name = PackageName::from_escaped_str(package_name)?; - - let version = Version::parse(version)?; - - let resolved_pkg = lockfile - .children - .get(&package_name) - .ok_or_else(|| ApplyPatchesError::PackageNotFound(package_name.clone()))? - .get(&version) - .ok_or_else(|| { - ApplyPatchesError::VersionNotFound(version.clone(), package_name.clone()) - })?; - - debug!("resolved package {package_name}@{version} to {resolved_pkg}"); - - let (_, source_path) = resolved_pkg.directory(self.path()); - let diff = Diff::from_buffer(&read(&path)?)?; - - let repo = match Repository::open(&source_path) { - Ok(repo) => repo, - Err(_) => setup_patches_repo(&source_path)?, - }; - - repo.apply(&diff, ApplyLocation::Both, None)?; - - let mut index = repo.index()?; - index.add_all(["*"].iter(), git2::IndexAddOption::DEFAULT, None)?; - index.write()?; - - let signature = make_signature()?; - let tree_id = index.write_tree()?; - let tree = repo.find_tree(tree_id)?; - let parent = repo.head()?.peel_to_commit()?; - repo.commit( - Some("HEAD"), - &signature, - &signature, - "applied patches", - &tree, - &[&parent], - )?; - } - - Ok(()) - } -} diff --git a/src/project.rs b/src/project.rs deleted file mode 100644 index 5c50763..0000000 --- a/src/project.rs +++ /dev/null @@ -1,326 +0,0 @@ -use log::{error, warn}; -use std::{ - collections::HashMap, - fmt::Debug, - fs::{read, File}, - path::{Path, PathBuf}, -}; - -use thiserror::Error; -use url::Url; - -use crate::{ - dependencies::{resolution::RootLockfileNode, DownloadError, UrlResolveError}, - index::Index, - linking_file::LinkingDependenciesError, - manifest::{update_sync_tool_files, Manifest, ManifestReadError}, - LOCKFILE_FILE_NAME, -}; - -/// A map of indices -pub type Indices = HashMap>; - -/// A pesde project -#[derive(Debug)] -pub struct Project { - path: PathBuf, - cache_path: PathBuf, - indices: Indices, - manifest: Manifest, - pub(crate) reqwest_client: reqwest::blocking::Client, -} - -/// Options for installing a project -pub struct InstallOptions { - locked: bool, - auto_download: bool, - lockfile: Option, -} - -impl Default for InstallOptions { - fn default() -> Self { - Self { - locked: false, - auto_download: true, - lockfile: None, - } - } -} - -impl InstallOptions { - /// Creates a new set of install options (uses the Default implementation) - pub fn new() -> Self { - Self::default() - } - - /// Makes the installation to use the lockfile, and ensure that the lockfile is up-to-date - pub fn locked(&self, locked: bool) -> Self { - Self { - locked, - lockfile: self.lockfile.clone(), - ..*self - } - } - - /// Makes the installation to automatically download the dependencies - /// Having this set to false is only useful if you want to download the dependencies yourself. An example of this is the CLI's progress bar - pub fn auto_download(&self, auto_download: bool) -> Self { - Self { - auto_download, - lockfile: self.lockfile.clone(), - ..*self - } - } - - /// Makes the installation to use the given lockfile - /// Having this set to Some is only useful if you're using auto_download = false - pub fn lockfile(&self, lockfile: RootLockfileNode) -> Self { - Self { - lockfile: Some(lockfile), - ..*self - } - } -} - -/// An error that occurred while reading the lockfile -#[derive(Debug, Error)] -pub enum ReadLockfileError { - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while deserializing the lockfile - #[error("error deserializing lockfile")] - LockfileDeser(#[source] serde_yaml::Error), -} - -/// An error that occurred while downloading a project -#[derive(Debug, Error)] -pub enum InstallProjectError { - /// An error that occurred while resolving the dependency graph - #[error("failed to resolve dependency graph")] - ResolveGraph(#[from] crate::dependencies::resolution::ResolveError), - - /// An error that occurred while downloading a package - #[error("failed to download package")] - DownloadPackage(#[from] DownloadError), - - /// An error that occurred while applying patches - #[error("error applying patches")] - ApplyPatches(#[from] crate::patches::ApplyPatchesError), - - /// An error that occurred while linking dependencies - #[error("failed to link dependencies")] - Linking(#[from] LinkingDependenciesError), - - /// An error that occurred while interacting with the file system - #[error("error interacting with the file system")] - Io(#[from] std::io::Error), - - /// An error that occurred while writing the lockfile - #[error("failed to write lockfile")] - LockfileSer(#[source] serde_yaml::Error), - - /// An error that occurred while resolving the url of a package - #[error("failed to resolve package URL")] - UrlResolve(#[from] UrlResolveError), - - /// An error that occurred while reading the lockfile - #[error("failed to read lockfile")] - ReadLockfile(#[from] ReadLockfileError), -} - -/// The name of the default index to use -pub const DEFAULT_INDEX_NAME: &str = "default"; - -pub(crate) fn get_index<'a>(indices: &'a Indices, index_name: Option<&str>) -> &'a dyn Index { - indices - .get(index_name.unwrap_or(DEFAULT_INDEX_NAME)) - .or_else(|| { - warn!( - "index `{}` not found, using default index", - index_name.unwrap_or("") - ); - indices.get(DEFAULT_INDEX_NAME) - }) - .unwrap() - .as_ref() -} - -pub(crate) fn get_index_by_url<'a>(indices: &'a Indices, url: &Url) -> &'a dyn Index { - indices - .values() - .find(|index| index.url() == url) - .map(|index| index.as_ref()) - .unwrap_or_else(|| get_index(indices, None)) -} - -#[cfg(feature = "wally")] -pub(crate) fn get_wally_index<'a>( - indices: &'a mut Indices, - url: &Url, - path: Option<&Path>, -) -> Result<&'a crate::index::WallyIndex, crate::index::RefreshError> { - if !indices.contains_key(url.as_str()) { - let default_index = indices.get(DEFAULT_INDEX_NAME).unwrap(); - let default_token = default_index.registry_auth_token().map(|t| t.to_string()); - let default_credentials_fn = default_index.credentials_fn().cloned(); - - let index = crate::index::WallyIndex::new( - url.clone(), - default_token, - path.expect("index should already exist by now"), - default_credentials_fn, - ); - - match index.refresh() { - Ok(_) => { - indices.insert(url.as_str().to_string(), Box::new(index)); - } - Err(e) => { - error!("failed to refresh wally index: {e}"); - return Err(e); - } - } - } - - Ok(indices - .get(url.as_str()) - .unwrap() - .as_any() - .downcast_ref() - .unwrap()) -} - -/// An error that occurred while creating a new project -#[derive(Debug, Error)] -pub enum NewProjectError { - /// A default index was not provided - #[error("default index not provided")] - DefaultIndexNotProvided, -} - -/// An error that occurred while creating a project from a path -#[derive(Debug, Error)] -pub enum ProjectFromPathError { - /// An error that occurred while reading the manifest - #[error("error reading manifest")] - ManifestRead(#[from] ManifestReadError), - - /// An error that occurred while creating the project - #[error("error creating project")] - NewProject(#[from] NewProjectError), -} - -impl Project { - /// Creates a new project - pub fn new, Q: AsRef>( - path: P, - cache_path: Q, - indices: Indices, - manifest: Manifest, - ) -> Result { - if !indices.contains_key(DEFAULT_INDEX_NAME) { - return Err(NewProjectError::DefaultIndexNotProvided); - } - - Ok(Self { - path: path.as_ref().to_path_buf(), - cache_path: cache_path.as_ref().to_path_buf(), - indices, - manifest, - reqwest_client: reqwest::blocking::ClientBuilder::new() - .user_agent(concat!( - env!("CARGO_PKG_NAME"), - "/", - env!("CARGO_PKG_VERSION") - )) - .build() - .unwrap(), - }) - } - - /// Creates a new project from a path (manifest will be read from the path) - pub fn from_path, Q: AsRef>( - path: P, - cache_path: Q, - indices: Indices, - ) -> Result { - let manifest = Manifest::from_path(path.as_ref())?; - - Ok(Self::new(path, cache_path, indices, manifest)?) - } - - /// Returns the indices of the project - pub fn indices(&self) -> &HashMap> { - &self.indices - } - - #[cfg(feature = "wally")] - pub(crate) fn indices_mut(&mut self) -> &mut HashMap> { - &mut self.indices - } - - /// Returns the manifest of the project - pub fn manifest(&self) -> &Manifest { - &self.manifest - } - - /// Returns the cache directory of the project - pub fn cache_dir(&self) -> &Path { - &self.cache_path - } - - /// Returns the path of the project - pub fn path(&self) -> &Path { - &self.path - } - - /// Returns the lockfile of the project - pub fn lockfile(&self) -> Result, ReadLockfileError> { - let lockfile_path = self.path.join(LOCKFILE_FILE_NAME); - - Ok(if lockfile_path.exists() { - let lockfile_contents = read(&lockfile_path)?; - let lockfile: RootLockfileNode = serde_yaml::from_slice(&lockfile_contents) - .map_err(ReadLockfileError::LockfileDeser)?; - - Some(lockfile) - } else { - None - }) - } - - /// Downloads the project's dependencies, applies patches, and links the dependencies - pub fn install(&mut self, install_options: InstallOptions) -> Result<(), InstallProjectError> { - let old_lockfile = self.lockfile()?; - - let lockfile = match install_options.lockfile { - Some(map) => map, - None => { - let manifest = self.manifest.clone(); - - manifest.dependency_graph(self, install_options.locked)? - } - }; - - if install_options.auto_download { - self.download(&lockfile)?.wait()?; - } - - self.apply_patches(&lockfile)?; - - self.link_dependencies(&lockfile)?; - - if !install_options.locked { - serde_yaml::to_writer(File::create(self.path.join(LOCKFILE_FILE_NAME))?, &lockfile) - .map_err(InstallProjectError::LockfileSer)?; - } - - if !old_lockfile.is_some_and(|old| old.name == lockfile.name) { - update_sync_tool_files(self.path(), lockfile.name.name().to_string())?; - } - - Ok(()) - } -} diff --git a/src/source/mod.rs b/src/source/mod.rs new file mode 100644 index 0000000..0e0e440 --- /dev/null +++ b/src/source/mod.rs @@ -0,0 +1,79 @@ +use std::{collections::BTreeMap, fmt::Debug, path::Path}; + +use semver::Version; +use serde::{Deserialize, Serialize}; + +use crate::Project; + +pub mod pesde; + +pub trait DependencySpecifier: Debug { + fn alias(&self) -> &str; + fn set_alias(&mut self, alias: String); +} + +pub trait PackageRef: Debug {} + +pub(crate) fn hash(struc: &S) -> String { + use std::collections::hash_map::DefaultHasher; + use std::hash::Hasher; + + let mut hasher = DefaultHasher::new(); + struc.hash(&mut hasher); + hasher.finish().to_string() +} + +pub trait PackageSource: Debug { + type Ref: PackageRef; + type Specifier: DependencySpecifier; + type RefreshError: std::error::Error; + type ResolveError: std::error::Error; + type DownloadError: std::error::Error; + + fn refresh(&self, _project: &Project) -> Result<(), Self::RefreshError> { + Ok(()) + } + + fn resolve( + &self, + specifier: &Self::Specifier, + project: &Project, + ) -> Result, Self::ResolveError>; + + fn download( + &self, + pkg_ref: &Self::Ref, + destination: &Path, + project: &Project, + ) -> Result<(), Self::DownloadError>; +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +#[serde(untagged)] +pub enum DependencySpecifiers { + Pesde(pesde::PesdeDependencySpecifier), +} + +impl DependencySpecifiers { + pub fn alias(&self) -> &str { + match self { + DependencySpecifiers::Pesde(spec) => spec.alias(), + } + } + + pub fn set_alias(&mut self, alias: String) { + match self { + DependencySpecifiers::Pesde(spec) => spec.set_alias(alias), + } + } +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub enum PackageRefs { + Pesde(pesde::PesdePackageRef), +} + +#[derive(Debug, Eq, PartialEq, Hash)] +pub enum PackageSources { + Pesde(pesde::PesdePackageSource), +} diff --git a/src/source/pesde.rs b/src/source/pesde.rs new file mode 100644 index 0000000..324031c --- /dev/null +++ b/src/source/pesde.rs @@ -0,0 +1,588 @@ +use std::{collections::BTreeMap, fmt::Debug, hash::Hash, path::Path}; + +use gix::remote::Direction; +use semver::{Version, VersionReq}; +use serde::{Deserialize, Serialize}; + +use crate::{ + authenticate_conn, + manifest::Target, + names::PackageName, + source::{hash, DependencySpecifier, DependencySpecifiers, PackageRef, PackageSource}, + Project, REQWEST_CLIENT, +}; + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub struct PesdeDependencySpecifier { + pub name: PackageName, + pub version: VersionReq, + #[serde(default, skip_serializing_if = "String::is_empty")] + pub alias: String, +} + +impl DependencySpecifier for PesdeDependencySpecifier { + fn alias(&self) -> &str { + self.alias.as_str() + } + + fn set_alias(&mut self, alias: String) { + self.alias = alias; + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)] +pub struct PesdePackageRef { + name: PackageName, + version: Version, +} + +impl PackageRef for PesdePackageRef {} + +impl Ord for PesdePackageRef { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.version.cmp(&other.version) + } +} + +impl PartialOrd for PesdePackageRef { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +#[derive(Debug, Hash, PartialEq, Eq)] +pub struct PesdePackageSource { + repo_url: gix::Url, +} + +const OWNERS_FILE: &str = "owners.yaml"; + +impl PesdePackageSource { + pub fn new(repo_url: gix::Url) -> Self { + Self { repo_url } + } + + pub fn path(&self, project: &Project) -> std::path::PathBuf { + project.data_dir.join("indices").join(hash(self)) + } + + pub(crate) fn tree<'a>( + &'a self, + repo: &'a gix::Repository, + ) -> Result> { + // this is a bare repo, so this is the actual path + let path = repo.path().to_path_buf(); + + let remote = match repo.find_default_remote(Direction::Fetch) { + Some(Ok(remote)) => remote, + Some(Err(e)) => return Err(Box::new(errors::TreeError::GetDefaultRemote(path, e))), + None => { + return Err(Box::new(errors::TreeError::NoDefaultRemote(path))); + } + }; + + let refspec = match remote.refspecs(Direction::Fetch).first() { + Some(head) => head, + None => return Err(Box::new(errors::TreeError::NoRefSpecs(path))), + }; + + let spec_ref = refspec.to_ref(); + let local_ref = match spec_ref.local() { + Some(local) => local + .to_string() + .replace('*', repo.branch_names().first().unwrap_or(&"main")), + None => return Err(Box::new(errors::TreeError::NoLocalRefSpec(path))), + }; + + let reference = match repo.find_reference(&local_ref) { + Ok(reference) => reference, + Err(e) => { + return Err(Box::new(errors::TreeError::NoReference( + local_ref.to_string(), + e, + ))) + } + }; + + let reference_name = reference.name().as_bstr().to_string(); + let id = match reference.into_fully_peeled_id() { + Ok(id) => id, + Err(e) => return Err(Box::new(errors::TreeError::CannotPeel(reference_name, e))), + }; + + let id_str = id.to_string(); + let object = match id.object() { + Ok(object) => object, + Err(e) => { + return Err(Box::new(errors::TreeError::CannotConvertToObject( + id_str, e, + ))) + } + }; + + match object.peel_to_tree() { + Ok(tree) => Ok(tree), + Err(e) => Err(Box::new(errors::TreeError::CannotPeelToTree(id_str, e))), + } + } + + pub(crate) fn read_file< + I: IntoIterator + Clone, + P: ToString + PartialEq, + >( + &self, + file_path: I, + project: &Project, + ) -> Result>, Box> { + let path = self.path(project); + + let repo = match gix::open(&path) { + Ok(repo) => repo, + Err(e) => return Err(Box::new(errors::ReadFile::Open(path, e))), + }; + + let tree = match self.tree(&repo) { + Ok(tree) => tree, + Err(e) => return Err(Box::new(errors::ReadFile::Tree(path, e))), + }; + + let file_path_str = file_path + .clone() + .into_iter() + .map(|s| s.to_string()) + .collect::>() + .join(std::path::MAIN_SEPARATOR_STR); + + let mut lookup_buf = vec![]; + let entry = match tree.lookup_entry(file_path, &mut lookup_buf) { + Ok(Some(entry)) => entry, + Ok(None) => return Ok(None), + Err(e) => return Err(Box::new(errors::ReadFile::Lookup(file_path_str, e))), + }; + + let object = match entry.object() { + Ok(object) => object, + Err(e) => return Err(Box::new(errors::ReadFile::Lookup(file_path_str, e))), + }; + + let blob = object.into_blob(); + Ok(Some(blob.data.clone())) + } + + pub fn config(&self, project: &Project) -> Result> { + let file = self + .read_file(["config.yaml"], project) + .map_err(|e| Box::new(e.into()))?; + let bytes = match file { + Some(bytes) => bytes, + None => { + return Err(Box::new(errors::ConfigError::Missing( + self.repo_url.clone(), + ))) + } + }; + + let config: IndexConfig = serde_yaml::from_slice(&bytes).map_err(|e| Box::new(e.into()))?; + + Ok(config) + } + + pub fn all_packages( + &self, + project: &Project, + ) -> Result, Box> { + let path = self.path(project); + + let repo = match gix::open(&path) { + Ok(repo) => repo, + Err(e) => return Err(Box::new(errors::AllPackagesError::Open(path, e))), + }; + + let tree = match self.tree(&repo) { + Ok(tree) => tree, + Err(e) => return Err(Box::new(errors::AllPackagesError::Tree(path, e))), + }; + + let mut packages = BTreeMap::::new(); + + for entry in tree.iter() { + let entry = match entry { + Ok(entry) => entry, + Err(e) => return Err(Box::new(errors::AllPackagesError::Decode(path, e))), + }; + + let object = match entry.object() { + Ok(object) => object, + Err(e) => return Err(Box::new(errors::AllPackagesError::Convert(path, e))), + }; + + // directories will be trees, and files will be blobs + if !matches!(object.kind, gix::object::Kind::Tree) { + continue; + } + + let package_scope = entry.filename().to_string(); + + for inner_entry in object.into_tree().iter() { + let inner_entry = match inner_entry { + Ok(entry) => entry, + Err(e) => return Err(Box::new(errors::AllPackagesError::Decode(path, e))), + }; + + let object = match inner_entry.object() { + Ok(object) => object, + Err(e) => return Err(Box::new(errors::AllPackagesError::Convert(path, e))), + }; + + if !matches!(object.kind, gix::object::Kind::Blob) { + continue; + } + + let package_name = inner_entry.filename().to_string(); + + if package_name == OWNERS_FILE { + continue; + } + + let blob = object.into_blob(); + let file: IndexFileEntry = match serde_yaml::from_slice(&blob.data) { + Ok(file) => file, + Err(e) => { + return Err(Box::new(errors::AllPackagesError::Deserialize( + package_name, + path, + e, + ))) + } + }; + + // if this panics, it's an issue with the index. + let name = format!("{package_scope}/{package_name}").parse().unwrap(); + + packages + .entry(name) + .or_default() + .insert(file.version.clone(), file); + } + } + + Ok(packages) + } +} + +impl PackageSource for PesdePackageSource { + type Ref = PesdePackageRef; + type Specifier = PesdeDependencySpecifier; + type RefreshError = errors::RefreshError; + type ResolveError = errors::ResolveError; + type DownloadError = errors::DownloadError; + + fn refresh(&self, project: &Project) -> Result<(), Self::RefreshError> { + let path = self.path(project); + if path.exists() { + let repo = match gix::open(&path) { + Ok(repo) => repo, + Err(e) => return Err(Self::RefreshError::Open(path, e)), + }; + let remote = match repo.find_default_remote(Direction::Fetch) { + Some(Ok(remote)) => remote, + Some(Err(e)) => return Err(Self::RefreshError::GetDefaultRemote(path, e)), + None => { + return Err(Self::RefreshError::NoDefaultRemote(path)); + } + }; + + let mut connection = remote + .connect(Direction::Fetch) + .map_err(|e| Self::RefreshError::Connect(self.repo_url.clone(), e))?; + + authenticate_conn(&mut connection, project.auth_config.clone()); + + connection + .prepare_fetch(gix::progress::Discard, Default::default()) + .map_err(|e| Self::RefreshError::PrepareFetch(self.repo_url.clone(), e))? + .receive(gix::progress::Discard, &false.into()) + .map_err(|e| Self::RefreshError::Read(self.repo_url.clone(), e))?; + + return Ok(()); + } + + std::fs::create_dir_all(&path)?; + let auth_config = project.auth_config.clone(); + + gix::prepare_clone_bare(self.repo_url.clone(), &path) + .map_err(|e| Self::RefreshError::Clone(self.repo_url.clone(), e))? + .configure_connection(move |c| { + authenticate_conn(c, auth_config.clone()); + Ok(()) + }) + .fetch_only(gix::progress::Discard, &false.into()) + .map_err(|e| Self::RefreshError::Fetch(self.repo_url.clone(), e))?; + + Ok(()) + } + + fn resolve( + &self, + specifier: &Self::Specifier, + project: &Project, + ) -> Result, Self::ResolveError> { + let (scope, name) = specifier.name.as_str(); + let bytes = match self.read_file([scope, name], project) { + Ok(Some(bytes)) => bytes, + Ok(None) => return Ok(BTreeMap::new()), + Err(e) => return Err(Self::ResolveError::Read(specifier.name.to_string(), e)), + }; + + let entries: Vec = serde_yaml::from_slice(&bytes) + .map_err(|e| Self::ResolveError::Parse(specifier.name.to_string(), e))?; + + Ok(entries + .into_iter() + .filter(|entry| specifier.version.matches(&entry.version)) + .map(|entry| { + ( + entry.version.clone(), + PesdePackageRef { + name: specifier.name.clone(), + version: entry.version, + }, + ) + }) + .collect()) + } + + fn download( + &self, + pkg_ref: &Self::Ref, + destination: &Path, + project: &Project, + ) -> Result<(), Self::DownloadError> { + let config = self.config(project)?; + + let (scope, name) = pkg_ref.name.as_str(); + let url = config + .download() + .replace("{PACKAGE_SCOPE}", scope) + .replace("{PACKAGE_NAME}", name) + .replace("{PACKAGE_VERSION}", &pkg_ref.version.to_string()); + + let mut response = REQWEST_CLIENT.get(url); + + if let Some(token) = &project.auth_config.pesde_token { + use secrecy::ExposeSecret; + response = + response.header("Authorization", format!("Bearer {}", token.expose_secret())); + } + + let response = response.send()?; + let bytes = response.bytes()?; + + let mut decoder = flate2::read::GzDecoder::new(bytes.as_ref()); + let mut archive = tar::Archive::new(&mut decoder); + + archive.unpack(destination)?; + + Ok(()) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone)] +#[serde(deny_unknown_fields)] +pub struct IndexConfig { + pub api: url::Url, + pub download: Option, + #[serde(default)] + pub git_allowed: bool, + #[serde(default)] + pub custom_registry_allowed: bool, + pub github_oauth_client_id: String, +} + +impl IndexConfig { + pub fn api(&self) -> &str { + self.api.as_str().trim_end_matches('/') + } + + pub fn download(&self) -> String { + self.download + .as_ref() + .unwrap_or( + &"{API_URL}/v0/packages/{PACKAGE_SCOPE}/{PACKAGE_NAME}/{PACKAGE_VERSION}" + .to_string(), + ) + .replace("{API_URL}", self.api()) + } +} + +#[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] +pub struct IndexFileEntry { + pub version: Version, + pub target: Target, + #[serde(default = "chrono::Utc::now")] + pub published_at: chrono::DateTime, + + #[serde(default, skip_serializing_if = "Option::is_none")] + pub description: Option, + + #[serde(default, skip_serializing_if = "Vec::is_empty")] + pub dependencies: Vec, +} + +impl Ord for IndexFileEntry { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.target + .cmp(&other.target) + .then_with(|| self.version.cmp(&other.version)) + } +} + +impl PartialOrd for IndexFileEntry { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +pub type IndexFile = BTreeMap; + +pub mod errors { + use std::path::PathBuf; + + use thiserror::Error; + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum RefreshError { + #[error("error interacting with the filesystem")] + Io(#[from] std::io::Error), + + #[error("error opening repository at {0}")] + Open(PathBuf, gix::open::Error), + + #[error("no default remote found in repository at {0}")] + NoDefaultRemote(PathBuf), + + #[error("error getting default remote from repository at {0}")] + GetDefaultRemote(PathBuf, gix::remote::find::existing::Error), + + #[error("error connecting to remote repository at {0}")] + Connect(gix::Url, gix::remote::connect::Error), + + #[error("error preparing fetch from remote repository at {0}")] + PrepareFetch(gix::Url, gix::remote::fetch::prepare::Error), + + #[error("error reading from remote repository at {0}")] + Read(gix::Url, gix::remote::fetch::Error), + + #[error("error cloning repository from {0}")] + Clone(gix::Url, gix::clone::Error), + + #[error("error fetching repository from {0}")] + Fetch(gix::Url, gix::clone::fetch::Error), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum TreeError { + #[error("error interacting with the filesystem")] + Io(#[from] std::io::Error), + + #[error("error opening repository at {0}")] + Open(PathBuf, gix::open::Error), + + #[error("no default remote found in repository at {0}")] + NoDefaultRemote(PathBuf), + + #[error("error getting default remote from repository at {0}")] + GetDefaultRemote(PathBuf, gix::remote::find::existing::Error), + + #[error("no refspecs found in repository at {0}")] + NoRefSpecs(PathBuf), + + #[error("no local refspec found in repository at {0}")] + NoLocalRefSpec(PathBuf), + + #[error("no reference found for local refspec {0}")] + NoReference(String, gix::reference::find::existing::Error), + + #[error("cannot peel reference {0}")] + CannotPeel(String, gix::reference::peel::Error), + + #[error("error converting id {0} to object")] + CannotConvertToObject(String, gix::object::find::existing::Error), + + #[error("error peeling object {0} to tree")] + CannotPeelToTree(String, gix::object::peel::to_kind::Error), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum ReadFile { + #[error("error opening repository at {0}")] + Open(PathBuf, gix::open::Error), + + #[error("error getting tree from repository at {0}")] + Tree(PathBuf, Box), + + #[error("error looking up entry {0} in tree")] + Lookup(String, gix::object::find::existing::Error), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum ResolveError { + #[error("error interacting with the filesystem")] + Io(#[from] std::io::Error), + + #[error("error reading file for {0}")] + Read(String, Box), + + #[error("error parsing file for {0}")] + Parse(String, serde_yaml::Error), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum ConfigError { + #[error("error reading config file")] + ReadFile(#[from] Box), + + #[error("error parsing config file")] + Parse(#[from] serde_yaml::Error), + + #[error("missing config file for index at {0}")] + Missing(gix::Url), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum AllPackagesError { + #[error("error opening repository at {0}")] + Open(PathBuf, gix::open::Error), + + #[error("error getting tree from repository at {0}")] + Tree(PathBuf, Box), + + #[error("error decoding entry in repository at {0}")] + Decode(PathBuf, gix::objs::decode::Error), + + #[error("error converting entry in repository at {0}")] + Convert(PathBuf, gix::object::find::existing::Error), + + #[error("error deserializing file {0} in repository at {1}")] + Deserialize(String, PathBuf, serde_yaml::Error), + } + + #[derive(Debug, Error)] + #[non_exhaustive] + pub enum DownloadError { + #[error("error reading config file")] + ReadFile(#[from] Box), + + #[error("error downloading package")] + Download(#[from] reqwest::Error), + + #[error("error unpacking package")] + Unpack(#[from] std::io::Error), + } +} diff --git a/tests/prelude.rs b/tests/prelude.rs deleted file mode 100644 index fdf52c9..0000000 --- a/tests/prelude.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::{ - any::Any, - collections::{BTreeSet, HashMap}, - sync::Arc, -}; -use url::Url; - -use pesde::{ - index::{ - ConfigError, CreatePackageVersionError, CredentialsFn, Index, IndexConfig, IndexFile, - IndexFileEntry, IndexPackageError, ScopeOwners, ScopeOwnersError, - }, - manifest::Manifest, - package_name::PackageName, -}; - -/// An in-memory implementation of the [`Index`] trait. Used for testing. -#[derive(Debug, Clone)] -pub struct InMemoryIndex { - packages: HashMap, IndexFile)>, - url: Url, -} - -impl Default for InMemoryIndex { - fn default() -> Self { - Self { - packages: HashMap::new(), - url: Url::parse("https://example.com").unwrap(), - } - } -} - -impl InMemoryIndex { - pub fn new() -> Self { - Self::default() - } - - pub fn with_scope(mut self, scope: &str, owners: BTreeSet) -> Self { - self.packages - .insert(scope.to_string(), (owners, IndexFile::default())); - self - } - - pub fn with_package(mut self, scope: &str, index_file: IndexFileEntry) -> Self { - self.packages - .entry(scope.to_string()) - .or_insert_with(|| (BTreeSet::new(), IndexFile::default())) - .1 - .insert(index_file); - self - } -} - -impl Index for InMemoryIndex { - fn scope_owners(&self, scope: &str) -> Result, ScopeOwnersError> { - Ok(self.packages.get(scope).map(|(owners, _)| owners).cloned()) - } - - fn create_scope_for( - &mut self, - scope: &str, - owners: &ScopeOwners, - ) -> Result { - self.packages - .insert(scope.to_string(), (owners.clone(), IndexFile::default())); - Ok(true) - } - - fn package(&self, name: &PackageName) -> Result, IndexPackageError> { - Ok(self - .packages - .get(name.scope()) - .map(|(_, file)| file.clone())) - } - - fn create_package_version( - &mut self, - manifest: &Manifest, - uploader: &u64, - ) -> Result, CreatePackageVersionError> { - let scope = manifest.name.scope(); - - if let Some(owners) = self.scope_owners(scope)? { - if !owners.contains(uploader) { - return Err(CreatePackageVersionError::MissingScopeOwnership); - } - } else if !self.create_scope_for(scope, &BTreeSet::from([*uploader]))? { - return Err(CreatePackageVersionError::MissingScopeOwnership); - } - - let package = self.packages.get_mut(scope).unwrap(); - - let entry: IndexFileEntry = manifest.clone().try_into()?; - package.1.insert(entry.clone()); - - Ok(Some(entry)) - } - - fn config(&self) -> Result { - Ok(IndexConfig { - download: None, - api: "http://127.0.0.1:8080".parse().unwrap(), - github_oauth_client_id: "".to_string(), - custom_registry_allowed: false, - git_allowed: false, - }) - } - - fn credentials_fn(&self) -> Option<&Arc> { - None - } - - fn url(&self) -> &Url { - &self.url - } - - fn as_any(&self) -> &dyn Any { - self - } -} diff --git a/tests/resolver.rs b/tests/resolver.rs deleted file mode 100644 index a7e748e..0000000 --- a/tests/resolver.rs +++ /dev/null @@ -1,141 +0,0 @@ -use std::collections::{BTreeMap, BTreeSet, HashMap}; - -use semver::Version; -use tempfile::tempdir; - -use pesde::{ - dependencies::{ - registry::{RegistryDependencySpecifier, RegistryPackageRef}, - resolution::ResolvedPackage, - DependencySpecifier, PackageRef, - }, - index::Index, - manifest::{DependencyType, Manifest, Realm}, - package_name::StandardPackageName, - project::{Project, DEFAULT_INDEX_NAME}, -}; -use prelude::*; - -mod prelude; - -#[test] -fn test_resolves_package() { - let dir = tempdir().unwrap(); - let dir_path = dir.path().to_path_buf(); - let index = InMemoryIndex::new(); - - let version_str = "0.1.0"; - let version: Version = version_str.parse().unwrap(); - let version_2_str = "0.1.1"; - let version_2: Version = version_2_str.parse().unwrap(); - - let description = "test package"; - - let pkg_name = StandardPackageName::new("test", "test").unwrap(); - - let pkg_manifest = Manifest { - name: pkg_name.clone(), - version: version.clone(), - exports: Default::default(), - path_style: Default::default(), - private: true, - realm: None, - indices: Default::default(), - #[cfg(feature = "wally")] - sourcemap_generator: None, - overrides: Default::default(), - - dependencies: Default::default(), - peer_dependencies: Default::default(), - description: Some(description.to_string()), - license: None, - authors: None, - repository: None, - }; - - let mut pkg_2_manifest = pkg_manifest.clone(); - pkg_2_manifest.version = version_2.clone(); - - let index = index - .with_scope(pkg_name.scope(), BTreeSet::from([0])) - .with_package(pkg_name.scope(), pkg_manifest.try_into().unwrap()) - .with_package(pkg_name.scope(), pkg_2_manifest.try_into().unwrap()); - - let specifier = DependencySpecifier::Registry(RegistryDependencySpecifier { - name: pkg_name.clone(), - version: format!("={version_str}").parse().unwrap(), - realm: None, - index: DEFAULT_INDEX_NAME.to_string(), - }); - let specifier_2 = DependencySpecifier::Registry(RegistryDependencySpecifier { - name: pkg_name.clone(), - version: format!(">{version_str}").parse().unwrap(), - realm: None, - index: DEFAULT_INDEX_NAME.to_string(), - }); - - let user_manifest = Manifest { - name: "test/user".parse().unwrap(), - version: version.clone(), - exports: Default::default(), - path_style: Default::default(), - private: true, - realm: None, - indices: Default::default(), - #[cfg(feature = "wally")] - sourcemap_generator: None, - overrides: Default::default(), - - dependencies: BTreeMap::from([("test".to_string(), specifier.clone())]), - peer_dependencies: BTreeMap::from([("test2".to_string(), specifier_2.clone())]), - description: Some(description.to_string()), - license: None, - authors: None, - repository: None, - }; - - let mut project = Project::new( - &dir_path, - &dir_path, - HashMap::from([( - DEFAULT_INDEX_NAME.to_string(), - Box::new(index.clone()) as Box, - )]), - user_manifest, - ) - .unwrap(); - - let manifest = project.manifest().clone(); - let graph = manifest.dependency_graph(&mut project, false).unwrap(); - assert_eq!(graph.children.len(), 1); - let versions = graph.children.get(&pkg_name.clone().into()).unwrap(); - assert_eq!(versions.len(), 2); - let resolved_pkg = versions.get(&version).unwrap(); - assert_eq!( - resolved_pkg, - &ResolvedPackage { - pkg_ref: PackageRef::Registry(RegistryPackageRef { - name: pkg_name.clone(), - version: version.clone(), - index_url: index.url().clone(), - }), - dependencies: Default::default(), - realm: Realm::Shared, - dep_type: DependencyType::Normal, - } - ); - let resolved_pkg_2 = versions.get(&version_2).unwrap(); - assert_eq!( - resolved_pkg_2, - &ResolvedPackage { - pkg_ref: PackageRef::Registry(RegistryPackageRef { - name: pkg_name.clone(), - version: version_2.clone(), - index_url: index.url().clone(), - }), - dependencies: Default::default(), - realm: Realm::Shared, - dep_type: DependencyType::Normal, - } - ); -} diff --git a/website/.eslintignore b/website/.eslintignore deleted file mode 100644 index 1a6c4db..0000000 --- a/website/.eslintignore +++ /dev/null @@ -1,9 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -pnpm-lock.yaml \ No newline at end of file diff --git a/website/.eslintrc.cjs b/website/.eslintrc.cjs deleted file mode 100644 index 0b75758..0000000 --- a/website/.eslintrc.cjs +++ /dev/null @@ -1,31 +0,0 @@ -/** @type { import("eslint").Linter.Config } */ -module.exports = { - root: true, - extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:svelte/recommended', - 'prettier' - ], - parser: '@typescript-eslint/parser', - plugins: ['@typescript-eslint'], - parserOptions: { - sourceType: 'module', - ecmaVersion: 2020, - extraFileExtensions: ['.svelte'] - }, - env: { - browser: true, - es2017: true, - node: true - }, - overrides: [ - { - files: ['*.svelte'], - parser: 'svelte-eslint-parser', - parserOptions: { - parser: '@typescript-eslint/parser' - } - } - ] -}; diff --git a/website/.gitignore b/website/.gitignore deleted file mode 100644 index 6635cf5..0000000 --- a/website/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -.DS_Store -node_modules -/build -/.svelte-kit -/package -.env -.env.* -!.env.example -vite.config.js.timestamp-* -vite.config.ts.timestamp-* diff --git a/website/.npmrc b/website/.npmrc deleted file mode 100644 index b6f27f1..0000000 --- a/website/.npmrc +++ /dev/null @@ -1 +0,0 @@ -engine-strict=true diff --git a/website/.prettierignore b/website/.prettierignore deleted file mode 100644 index cc41cea..0000000 --- a/website/.prettierignore +++ /dev/null @@ -1,4 +0,0 @@ -# Ignore files for PNPM, NPM and YARN -pnpm-lock.yaml -package-lock.json -yarn.lock diff --git a/website/.prettierrc b/website/.prettierrc deleted file mode 100644 index 6ed0b90..0000000 --- a/website/.prettierrc +++ /dev/null @@ -1,17 +0,0 @@ -{ - "useTabs": true, - "singleQuote": true, - "trailingComma": "none", - "printWidth": 100, - "plugins": [ - "prettier-plugin-svelte" - ], - "overrides": [ - { - "files": "*.svelte", - "options": { - "parser": "svelte" - } - } - ] -} \ No newline at end of file diff --git a/website/bun.lockb b/website/bun.lockb deleted file mode 100644 index daf647adb8064f678db66ca7012884c8706af265..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 151456 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 diff --git a/website/package.json b/website/package.json deleted file mode 100644 index 346d953..0000000 --- a/website/package.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "name": "website", - "version": "0.0.1", - "private": true, - "scripts": { - "dev": "vite dev", - "build": "vite build", - "preview": "vite preview", - "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json", - "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch", - "lint": "prettier --check . && eslint .", - "format": "prettier --write ." - }, - "devDependencies": { - "@shikijs/markdown-it": "^1.1.7", - "@sveltejs/adapter-auto": "^3.0.0", - "@sveltejs/kit": "^2.0.0", - "@sveltejs/vite-plugin-svelte": "^3.0.0", - "@types/eslint": "^8.56.0", - "@typescript-eslint/eslint-plugin": "^7.0.0", - "@typescript-eslint/parser": "^7.0.0", - "autoprefixer": "^10.4.18", - "eslint": "^8.56.0", - "eslint-config-prettier": "^9.1.0", - "eslint-plugin-svelte": "^2.35.1", - "postcss": "^8.4.35", - "prettier": "^3.1.1", - "prettier-plugin-svelte": "^3.1.2", - "svelte": "^4.2.7", - "svelte-check": "^3.6.0", - "tailwindcss": "^3.4.1", - "tslib": "^2.4.1", - "typescript": "^5.0.0", - "vite": "^5.0.3" - }, - "type": "module", - "dependencies": { - "@fontsource-variable/hepta-slab": "^5.0.19", - "@tailwindcss/typography": "^0.5.10", - "@types/markdown-it": "^13.0.7", - "@types/pako": "^2.0.3", - "@types/tar-stream": "^3.1.3", - "events": "^3.3.0", - "isomorphic-dompurify": "^2.4.0", - "lucide-svelte": "^0.358.0", - "markdown-it": "^14.0.0", - "pako": "^2.1.0", - "shiki": "^1.1.7", - "simple-svelte-autocomplete": "^2.5.2", - "tar-stream": "^3.1.7", - "yaml": "^2.4.1" - }, - "pnpm": { - "patchedDependencies": { - "tar-stream@3.1.7": "patches/tar-stream@3.1.7.patch", - "simple-svelte-autocomplete@2.5.2": "patches/simple-svelte-autocomplete@2.5.2.patch" - } - } -} \ No newline at end of file diff --git a/website/patches/simple-svelte-autocomplete@2.5.2.patch b/website/patches/simple-svelte-autocomplete@2.5.2.patch deleted file mode 100644 index 26dd9cb..0000000 --- a/website/patches/simple-svelte-autocomplete@2.5.2.patch +++ /dev/null @@ -1,16 +0,0 @@ -diff --git a/package.json b/package.json -index 0a796615e65323624ae9a1fdcc7c831f39dc5158..ac84dd37cf4c95223f2727d4522b8ceb74a48dff 100644 ---- a/package.json -+++ b/package.json -@@ -5,6 +5,11 @@ - "svelte": "src/SimpleAutocomplete.svelte", - "module": "dist/index.mjs", - "main": "dist/index.js", -+ "exports": { -+ ".": { -+ "svelte": "./src/SimpleAutocomplete.svelte" -+ } -+ }, - "devDependencies": { - "@babel/core": "^7.15.0", - "@babel/preset-env": "^7.16.11", diff --git a/website/patches/tar-stream@3.1.7.patch b/website/patches/tar-stream@3.1.7.patch deleted file mode 100644 index 314b56c..0000000 --- a/website/patches/tar-stream@3.1.7.patch +++ /dev/null @@ -1,9 +0,0 @@ -diff --git a/extract.js b/extract.js -index 0ed9f82bf287aa040dd560eabbe052316223011d..16f26d49a8b0eb554b7e0a79a076027612fb4ce1 100644 ---- a/extract.js -+++ b/extract.js -@@ -1,3 +1,4 @@ -+const EventEmitter = require('events') - const { Writable, Readable, getStreamError } = require('streamx') - const FIFO = require('fast-fifo') - const b4a = require('b4a') \ No newline at end of file diff --git a/website/postcss.config.js b/website/postcss.config.js deleted file mode 100644 index 2e7af2b..0000000 --- a/website/postcss.config.js +++ /dev/null @@ -1,6 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} diff --git a/website/src/app.css b/website/src/app.css deleted file mode 100644 index 74d6820..0000000 --- a/website/src/app.css +++ /dev/null @@ -1,19 +0,0 @@ -@tailwind base; -@tailwind components; -@tailwind utilities; - -@layer base { - html { - @apply font-serif; - } -} - -@layer utilities { - .overflow-text { - @apply min-w-0 whitespace-nowrap overflow-hidden text-ellipsis; - } -} - -a { - @apply text-links underline; -} diff --git a/website/src/app.d.ts b/website/src/app.d.ts deleted file mode 100644 index 743f07b..0000000 --- a/website/src/app.d.ts +++ /dev/null @@ -1,13 +0,0 @@ -// See https://kit.svelte.dev/docs/types#app -// for information about these interfaces -declare global { - namespace App { - // interface Error {} - // interface Locals {} - // interface PageData {} - // interface PageState {} - // interface Platform {} - } -} - -export {}; diff --git a/website/src/app.html b/website/src/app.html deleted file mode 100644 index 63c9a64..0000000 --- a/website/src/app.html +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - %sveltekit.head% - - - -

%sveltekit.body%
- - - \ No newline at end of file diff --git a/website/src/autocomplete.d.ts b/website/src/autocomplete.d.ts deleted file mode 100644 index 5251ab2..0000000 --- a/website/src/autocomplete.d.ts +++ /dev/null @@ -1,92 +0,0 @@ -// // https://github.com/pstanoev/simple-svelte-autocomplete/issues/205#issuecomment-1960396289 -declare module 'simple-svelte-autocomplete' { - import { SvelteComponent } from 'svelte'; - import { HTMLAttributes } from 'svelte/elements'; - - export interface AutoCompleteAttributes extends HTMLAttributes { - autocompleteOffValue?: string; - className?: string; - cleanUserText?: boolean; - closeOnBlur?: boolean; - create?: boolean; - createText?: string; - delay?: number; - disabled?: boolean; - dropdownClassName?: string; - flag?: boolean; - hideArrow?: boolean; - highlightedItem?: T; - html5autocomplete?: boolean; - ignoreAccents?: boolean; - inputClassName?: string; - inputId?: string; - items?: T[]; - keywordsFieldName?: string; - labelFieldName?: string; - localFiltering?: boolean; - localSorting?: boolean; - lock?: boolean; - lowercaseKeywords?: boolean; - matchAllKeywords?: boolean; - maxItemsToShowInList?: number; - minCharactersToSearch?: number; - moreItemsText?: string; - multiple?: boolean; - name?: string; - noInputClassName?: boolean; - noInputStyles?: boolean; - noResultsText?: string; - orderableSection?: boolean; - placeholder?: string; - readonly?: boolean; - required?: boolean; - selectFirstIfEmpty?: boolean; - selectName?: string; - selectedItem?: T; - showClear?: boolean; - showLoadingIndicator?: boolean; - sortByMatchedKeywords?: boolean; - tabIndex?: number; - value?: T; - valueFieldName?: string; - } - - export interface AutoCompleteFunctions { - itemFilterFunction?: (item: T, keywords: string) => boolean; - itemSortFunction?: (item1: T, item2: T, keywords: string) => number; - keywordsCleanFunction?: (keywords: string) => string; - keywordsFunction?: (item: T) => string; - labelFunction?: (item: T) => string; - searchFunction?: (keyword: string, maxItemsToShowInList: number) => Promise | boolean; - textCleanFunction?: (string) => string; - valueFunction?: (a: T) => string; - } - - export interface AutoCompleteCallbacks { - beforeChange?: (oldSelectedItem: T, newSelectedItem: T) => boolean; - onChange?: (newSelectedItem: T) => void; - onFocus?: () => void; - onBlur?: () => void; - onCreate?: (text: string) => void; - } - - export interface AutoCompleteSlots { - item: { item: T; label: string }; - 'no-results': null; - loading: { loadingText: string }; - tag: null; - 'dropdown-header': { nbItems: number; maxItemsToShowInList: number }; - 'dropdown-footer': { nbItems: number; maxItemsToShowInList: number }; - } - - export interface AutoCompleteProps - extends AutoCompleteAttributes, - AutoCompleteCallbacks, - AutoCompleteFunctions {} - - export default class AutoComplete extends SvelteComponent< - AutoCompleteProps, - undefined, - AutoCompleteSlots - > {} -} diff --git a/website/src/lib/Codeblock.svelte b/website/src/lib/Codeblock.svelte deleted file mode 100644 index d951714..0000000 --- a/website/src/lib/Codeblock.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - - -{#await codeToHtml(code, { theme: 'vesper', lang, transformers: [{ pre(node) { - this.addClassToHast(node, 'not-prose overflow-x-auto px-4 py-2 rounded-md'); - } }] }) then highlightedCode} - {@html highlightedCode} -{/await} diff --git a/website/src/lib/markdown.ts b/website/src/lib/markdown.ts deleted file mode 100644 index 2ebba48..0000000 --- a/website/src/lib/markdown.ts +++ /dev/null @@ -1,18 +0,0 @@ -import MarkdownIt from 'markdown-it'; -import Shiki from '@shikijs/markdown-it'; -import { writable } from 'svelte/store'; - -// nasty hack to get around the fact that @shikijs/markdown-it is async -export const md = writable(undefined); - -const it = MarkdownIt({ - html: true -}); - -Promise.all([Shiki({ theme: 'vesper' })]).then((plugins) => { - for (const plugin of plugins) { - it.use(plugin); - } - - md.set(it); -}); diff --git a/website/src/routes/+layout.svelte b/website/src/routes/+layout.svelte deleted file mode 100644 index f13117f..0000000 --- a/website/src/routes/+layout.svelte +++ /dev/null @@ -1,132 +0,0 @@ - - -
- - diff --git a/website/src/routes/+page.svelte b/website/src/routes/+page.svelte deleted file mode 100644 index 6185fda..0000000 --- a/website/src/routes/+page.svelte +++ /dev/null @@ -1,60 +0,0 @@ - - - - pesde - - - - - -
-

- pesde - the feature-rich Roblox package manager -

-
- {tagline} -
- -
- -
-

Recently published packages

- -
diff --git a/website/src/routes/+page.ts b/website/src/routes/+page.ts deleted file mode 100644 index 48f2f80..0000000 --- a/website/src/routes/+page.ts +++ /dev/null @@ -1,26 +0,0 @@ -import { error } from '@sveltejs/kit'; -import type { PageLoad } from './$types'; - -export const ssr = false; - -export const load: PageLoad = async ({ fetch }) => { - const latestRes = await fetch(`${import.meta.env.VITE_API_URL}/v0/search`); - - if (!latestRes.ok) { - error(latestRes.status, await latestRes.text()); - } - - const latest = (await latestRes.json()) as { - name: string; - version: string; - description?: string; - published_at: string; - }[]; - - return { - latest: latest.map((pkg) => ({ - ...pkg, - published_at: new Date(parseInt(pkg.published_at) * 1000) - })) - }; -}; diff --git a/website/src/routes/docs/+page.svelte b/website/src/routes/docs/+page.svelte deleted file mode 100644 index bfb1e00..0000000 --- a/website/src/routes/docs/+page.svelte +++ /dev/null @@ -1,258 +0,0 @@ - - - - pesde documentation - - - - - -
-

Using pesde

- -
-

Initializing a package

-

- Even if you're not making a package, but something else such as a game, you will still need to - initialize package. -

- -

This will prompt you questions, after which it will create a pesde.yaml file.

- - If you are using pesde with the `wally` feature enabled (true on releases from the GitHub - repository) then you can use to convert your wally.toml file - to pesde.yaml. This will leave you with an empty default index, so you will need to add a URL (such - as the default `https://github.com/daimond113/pesde-index`) yourself. - -
- -
-

Adding dependencies

-

- You can use the `add` command to add dependencies to your project. With the `wally` feature - enabled, you can add Wally dependencies. -

-

- If you are making a package, you can use the `--peer` argument to add a package as a peer - dependency. Peer dependencies are not installed when the package is installed, but are - required to be installed by the user of the package. This is useful for things like framework - plugins. -

-

- If you want to declare the dependency as server or development only, you can use the `--realm - server` or `--realm development` arguments respectively. The `shared` realm is the default. -

- -
- -
-

Overriding dependencies

-

- Dependency overrides allow you to use a different version of a dependency than the one - specified in the package. This is useful for sharing 1 version of a dependency. -

-

- Dependency overrides use the keys in the format of desired names separated with `>`, and - optionally other paths separated with `,`. The value is a dependency specifier. -

- - Dependency overrides do not have a command. You will need to edit the pesde.yaml file - yourself. - - -
- -
-

Removing dependencies

-

You can use the `remove` command to remove dependencies from your project.

- -
- -
-

Outdated dependencies

-

- You can list outdated dependencies with the `outdated` command. This will list all - dependencies that have a newer version available. -

- - This command only supports pesde registries, so neither Git nor Wally dependencies will be - listed. - - -
- -
-

Installing a project

-

The `install` command will install all dependencies of a project.

-

- You can use the `--locked` argument to skip resolving and read the dependencies from the - lockfile. If any changes were made from the time the lockfile was generated this will error. -

- -
- -
-

Running a bin dependency

-

- Dependencies may export a bin script. You can run this script with the `run` command. The - script will be executed with Lune. You can use the `--` argument to pass arguments to the - script. -

- - This does not support Wally dependencies. - - -
- -
-

Patching dependencies

-

- You can use the `patch` command to patch a dependency. This will output a directory in which - you can edit the dependency. After you are done, run the `patch-commit` command with the - directory as an argument to commit the changes. -

- -
- -
-

Publishing a package

-

- You can publish a package with the `publish` command. This will upload the package to the - registry. This will publish to the `default` index. -

- The official pesde registry does not support publishing packages with Wally or Git - dependencies. Dependency overrides and patches of your package as a dependency will be - ignored. - -

- Please look at the manifest format cheat sheet for more information about - the pesde.yaml file before publishing. -

-
- -
-

Searching for packages

-

- You can search for packages with the `search` command. This will list all packages that match - the query. It will search by name and description. -

- -
- -
-

Manifest format cheat sheet

-

- Here is a cheat sheet for the manifest format. This is the format of the pesde.yaml file. The - `name` and `version` fields are required. All other fields are optional. -

-

A description of each type:

-
    -
  • PACKAGE_NAME: either a STANDARD_PACKAGE_NAME or WALLY_PACKAGE_NAME
  • -
  • STANDARD_PACKAGE_NAME: refers to a package name used by pesde
  • -
  • - WALLY_PACKAGE_NAME: refers to a package name used by Wally. This will usually be prefixed - with `wally#`, although not required when this rather than `PACKAGE_NAME` is the type -
  • -
  • VERSION: a semver version specifier
  • -
  • VERSION_REQ: a semver version requirement
  • -
  • REALM: one of `shared`, `server`, or `development`
  • -
  • COMMAND: a command to run
  • -
  • - DEPENDENCY_SPECIFIER: one of REGISTRY_DEPENDENCY_SPECIFIER, GIT_DEPENDENCY_SPECIFIER, - WALLY_DEPENDENCY_SPECIFIER -
  • -
  • - REGISTRY_DEPENDENCY_SPECIFIER: an object with the following structure: - -
  • -
  • - GIT_DEPENDENCY_SPECIFIER: an object with the following structure: - -
  • -
  • - WALLY_DEPENDENCY_SPECIFIER: an object with the following structure: - -
  • -
- -

The exports field is used to specify the paths of the package's exports:

-
    -
  • - The `lib` field is a path to the file which will become the ModuleScript of the package. - This is only used for reading the types of the package. -
  • -
  • The `bin` field is a path to the file which will be ran with the `run` command.
  • -
-

- If the realm field is not specified, it will default to `shared`. If it is another value, and - the package is to be installed in a different realm, pesde will error. -

-

- The sourcemap generator command is only used for Wally and Git packages. It will be ran in a - package's directory, and must output a sourcemap file. This is used to generate a sourcemap - for the package so that types may be found and re-exported. -

-
-
diff --git a/website/src/routes/docs/Note.svelte b/website/src/routes/docs/Note.svelte deleted file mode 100644 index c0a15d9..0000000 --- a/website/src/routes/docs/Note.svelte +++ /dev/null @@ -1,10 +0,0 @@ - - -
-
Note
- -
diff --git a/website/src/routes/packages/[scope]/[name]/[version]/+page.svelte b/website/src/routes/packages/[scope]/[name]/[version]/+page.svelte deleted file mode 100644 index fb43a5b..0000000 --- a/website/src/routes/packages/[scope]/[name]/[version]/+page.svelte +++ /dev/null @@ -1,199 +0,0 @@ - - - - - - {data.scope}/{data.name}@{data.version} - - {#if data.description} - - - {/if} - - -
-
-
-

{data.scope}/{data.name}

- {#if data.description} -
{data.description}
- {/if} -
- -
{@html markdown}
-
-
-
-
-
- -
- - -
-
-
-
Published at
-
- -
-
-
-
Installation
- -
- {#if data.license} -
-
License
-
{data.license}
-
- {/if} - {#if data.repository} -
-
Repository
- {data.repository} -
- {/if} - {#if data.authors} -
-
Authors
-
    - {#each data.authors as author} -
  • - - {author.name} - -
    - {#if author.email} - - - - {/if} - {#if author.url} - - - - {/if} -
    -
  • - {/each} -
-
- {/if} - {#if data.realm} -
-
Realm
-
{data.realm}
-
- {/if} - {#each allDependencies as [dependencies, title]} - {#if dependencies && dependencies.length > 0} -
-
{title}
- -
- {/if} - {/each} -
-
Exports
-
    -
  • -
    - Library: - {#if data.exports.lib} - - {:else} - - {/if} -
    -
  • -
  • -
    - Binary: - {#if data.exports.bin} - - {:else} - - {/if} -
    -
  • -
-
-
-
-
- - diff --git a/website/src/routes/packages/[scope]/[name]/[version]/+page.ts b/website/src/routes/packages/[scope]/[name]/[version]/+page.ts deleted file mode 100644 index 4dab722..0000000 --- a/website/src/routes/packages/[scope]/[name]/[version]/+page.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { error, redirect } from '@sveltejs/kit'; -import type { PageLoad } from './$types'; -import { extract } from 'tar-stream'; -import { inflate } from 'pako'; -import { parse } from 'yaml'; - -export const ssr = false; - -type Dependencies = ({ name: string; version: string } | { repo: string; rev: string })[]; - -const parseAuthor = (author: string) => { - const authorRegex = - /^(?.+?)(?:\s*<(?.+?)>)?(?:\s*\((?.+?)\))?(?:\s*<(?.+?)>)?(?:\s*\((?.+?)\))?$/; - const { groups } = author.match(authorRegex) ?? {}; - return { - name: groups?.name ?? author, - email: groups?.email ?? groups?.email2, - url: groups?.url ?? groups?.url2 - }; -}; - -export const load: PageLoad = async ({ params, fetch }) => { - const res = await fetch( - `${import.meta.env.VITE_API_URL}/v0/packages/${params.scope}/${params.name}/${params.version}` - ); - - if (res.status === 404) { - error(res.status, 'Package not found'); - } else if (!res.ok) { - error(res.status, await res.text()); - } - - const body = await res.arrayBuffer(); - - const extractStream = extract(); - extractStream.end(inflate(body)); - - let manifestBuffer, readmeBuffer; - - for await (const entry of extractStream) { - const read = () => { - return new Promise((resolve, reject) => { - const chunks: number[] = []; - entry.on('data', (chunk: Uint8Array) => { - chunks.push(...chunk); - }); - entry.on('end', () => { - resolve(new Uint8Array(chunks)); - }); - entry.on('error', reject); - }); - }; - - switch (entry.header.name.toLowerCase()) { - case 'pesde.yaml': { - manifestBuffer = await read(); - break; - } - case 'readme.md': - case 'readme.txt': - case 'readme': { - readmeBuffer = await read(); - break; - } - } - - entry.resume(); - } - - if (!manifestBuffer) { - error(500, 'Package is missing pesde.yaml'); - } - - const textDecoder = new TextDecoder(); - - const manifest = textDecoder.decode(manifestBuffer); - const parsed = parse(manifest, { - customTags: [ - { - tag: '!roblox', - collection: 'map' - } - ] - }) as { - version: string; - authors?: string[]; - description?: string; - license?: string; - repository?: string; - realm?: string; - dependencies?: Dependencies; - peer_dependencies?: Dependencies; - exports?: { lib?: string; bin?: string }; - }; - - if (params.version.toLowerCase() === 'latest') { - redirect(302, `/packages/${params.scope}/${params.name}/${parsed.version}`); - } - - const readme = readmeBuffer ? textDecoder.decode(readmeBuffer) : null; - - const versionsRes = await fetch( - `${import.meta.env.VITE_API_URL}/v0/packages/${params.scope}/${params.name}/versions` - ); - - if (!versionsRes.ok) { - error(versionsRes.status, await versionsRes.text()); - } - - const versions = (await versionsRes.json()) as [string, number][]; - - return { - scope: params.scope, - name: params.name, - version: parsed.version, - versions: versions.map(([version]) => version), - publishedAt: new Date( - (versions.find(([version]) => version === parsed.version)?.[1] ?? 0) * 1000 - ), - authors: parsed.authors?.map(parseAuthor), - description: parsed.description, - license: parsed.license, - readme, - repository: parsed.repository, - realm: parsed.realm, - dependencies: parsed.dependencies, - peerDependencies: parsed.peer_dependencies, - exports: { - lib: !!parsed.exports?.lib, - bin: !!parsed.exports?.bin - } - }; -}; diff --git a/website/src/routes/policies/+page.svelte b/website/src/routes/policies/+page.svelte deleted file mode 100644 index 87acede..0000000 --- a/website/src/routes/policies/+page.svelte +++ /dev/null @@ -1,71 +0,0 @@ - - Policies - - - - - -
-

Policies for content on the public pesde registry

-

- If anything is unclear, please contact us and we will be - happy to help. -

- -
-

Permitted content

-

- The pesde registry is a place for open source Roblox packages. Examples of allowed content: -

-
    -
  • Libraries
  • -
  • Frameworks
  • -
- Examples of disallowed content: -
    -
  • Malicious code
  • -
  • Illegal content
  • -
- pesde is not responsible for the content of packages. If you believe a package is malicious or contains - illegal content, please - contact us. -
- -
-

Package removal

-

- pesde does not support removing packages from the registry without a reason such as security - or complying with the law. In case you published a secret to the registry, you must regenerate - it. If you believe a package should be removed, please contact us. We will review your request and take action if necessary. -

-

- If we find that a package is breaking the permitted content policy, we will remove it from the - registry without notice. -

-

- pesde reserves the right to remove any package from the registry at any time for any reason. -

-
- -
-

Package ownership

-

- Packages are owned by scopes. The first person to publish to the scope owns the scope. If you - want to work as a team, the owner of the scope must send a pull request to the index repo adding the members' user IDs to the scope's `owners.yaml` file. -

-
- -
-

Scope squatting

-

- Scope squatting is the act of creating a scope with the intent of preventing others from using - it. Scope squatting is not allowed. If you believe a scope is being squatted, please - contact us. We will review your request and take - action if necessary. -

-
-
diff --git a/website/static/android-chrome-192x192.png b/website/static/android-chrome-192x192.png deleted file mode 100644 index 0385ed4d05c798e72f578dd43ba8254c28c1a8e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14056 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 diff --git a/website/static/android-chrome-512x512.png b/website/static/android-chrome-512x512.png deleted file mode 100644 index 3bdfa0250b32d57dec29b178ddcf64ca49269436..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10121 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 diff --git a/website/static/apple-touch-icon.png b/website/static/apple-touch-icon.png deleted file mode 100644 index f19772fb3aeefc1deee5c710401c7fa9251adc1f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11465 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 diff --git a/website/static/browserconfig.xml b/website/static/browserconfig.xml deleted file mode 100644 index b3930d0..0000000 --- a/website/static/browserconfig.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - #da532c - - - diff --git a/website/static/favicon-16x16.png b/website/static/favicon-16x16.png deleted file mode 100644 index 5fa0cde46a40fbbd2484a50e19314cee67de561a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1040 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 ZbZcM*0TpbMZEwpZ0B5QjrH;%>{tu?K0!;t_ diff --git a/website/static/favicon.ico b/website/static/favicon.ico deleted file mode 100644 index 13af67590c678a507b587e0d6af68c6fe7511180..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 15086 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 diff --git a/website/static/logo.png b/website/static/logo.png deleted file mode 100644 index ef31b8750496559424f05ac9160cd045e3bc05d9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 14909 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�-B&=R?67Km5gbow>uL@7A?)S@* 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 y{~OZ(TcKkHG - - - - - - diff --git a/website/static/mstile-150x150.png b/website/static/mstile-150x150.png deleted file mode 100644 index f269d5c95c35568acfb96aa0d226ebce4faf0d4a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8427 zcmch7XH=725N1e#&_Q~KPm$hh=qd^V0@8zkbmp7--m%77(rGZ76KPq->Vi^L7>WX<`WM(;JdijO&bdkC|VK(LO%k5j({QbFAyj~ z83bB$2Z40nfIxf*Y?HMf@B_W4xyg0V`G4>Ct;HF@2sGT(;s*2=gpE%@38J_`0fEF) zO|M_IL(Xn4pklp!@+jLQwoYrbIXC?3h8fixM)yqRq@P_>Lz=XvC8(87i->US(+OkN zcQ|i%mkdgOQVV%nfYwoDxTqXv^7Y@R*Gh(7O`xwuO=wTLLs$vns}gxX8{!Pu8%jhx+YePyau?Znn7)Auk$VN`|sm@H1e1!6V>3=n=XV zvw*&Fg?*GYJgYsNg`fTjx)?2metv}!4lR{AuNOtCGZcJiiFbG#$0WRayJ_+6YA-&L zJ}dec^e*}|M!bTrf_b!O@JBp*5Jc0f!&^ZaRQf=f&34UmvcNBke~xP1O(yCVVt`o`uf&8u!)daMgv!K&Veq8g5kkgqS8w&oiWXrK=e9w zp6}0%0u;NMU;%E-c)SCK3#Fw>ZZLTQHNtw-5w_?;(nLLr# ze;!j5CLu}y&4ad>^y;B;14&^zwd3M|I=9*Qm1u@RcNsSfo_7U7L?N6(8b2{R?Luf1 z^w4l6&&}J(i_c(i_%5HKVPzwQ5h85a-EUR)?oY$N*< zS6#l?lKeb=ldeb&i?Fru!FZij%ciBN92J5MB5=C-bzyc$?z7gRMty!o)^POPWXv92 zKBR>MChXiZlUQQi7R9)B4&sxX@FWj4gb%TdZTLUic@$&BQdsN58d54-SuBMteTbPWM1@8iKz@rcL$`{Uj*wKCF#6rZjKMLoI17I~|?`kl(OC zronuTm1)f>Q2AZdx$wWZG0y(qr28O)1PgTswUi-8mqpU-aDvzdeEf$){H-n_daPJf zE1Y@&4Kn!X0CIx3{9M%4Hwzre1DgzwOR!}f=kBX?9^ocLuVGtM z=5H=s$tUd1I-Uc0s?}Bg;J%na=Eq>Xuz+A{RA}06rTnOw|BMwlTc_b8zj6Ai{))p3 z$!#^X8ixJ0c333s!K;1jj|sluVL%K6h)F}AGgrK~mWg?wK=o)jw;r7?w_7(IAHO0+ z%wutRwE4WRij`S(RR%d|K$qsE`MO(Iw|V}dSY~3c8EH^FHDoBJrA*#psQ_(W(W>lJ zN8?6o@O3V0#{OFjXxFL9BVueO*0@&g`8n9qe)QAO@JmA;U0qiHM;9BG;KSYu4ysHJT zJv}Z#WSukFX(yZ+ty#*~o)z~fM@y^+CcUyB9;@Y_4@6g2cpAEg{X>Chva|Q{@k;G` zhn^)N_gzZc`o4Ew8KE`EVe7rmdc-?b)G_nkIrAR1MYN>&EjgO>O56qR|MT~gNjiCf z^}l)|>b)3>E=-1@;JyQsAV`1lg8{>_7-Q$J2PAJ&uc=iOjTkk}(E^Cd`grb>%LMI_ z2f}I7T1)4{g!`m zDqPEFH|+Au+S%4wt2p%e4(3eHS{pA+x|np5#aT~vh$R6&Mmm~<4%iFL0eL7B^JToAQGaV z;!z`z`)1)qXlg5FpG7M4Qlo@wvV(n5AL`r@?m8%+&4+E*r>Iy?BUDGpq0OEHInC5? zV$XHs)q5mUVWX?}Yg~tK9BPt($t1NHFceq}@#iKQq7GQ=T9_;NVYCf_Yk|xmvE&9N zm$biM-mbyfm1six7rE>8>C*i9tC~I(XDpl?hLD67S|=zGKoU8FmkquBV}5AMVJx&3 z7m9kdO^354o^fP@V?6sDx!R?c3Bh!jI$D@EEBHZ+G?_MBs*g0eoN}SkHC&67?gL2DWfQNf{XDln;00 z)vsX+t`e(R>a6r+g`j6;B6a=6rt|EvoX-8F^@*|iP3Y5NJzjT5)#}elvI-K3%F!^3 zB{?)-l@~fT6R!YSauQtz+;FU6+Fe7Us0rGjL+O|VHfR8C{`S;xrSD0F^RpRv&pJ*r zFi@kUiFZKZ?NL|~e5xF50^x=U0vSit&QhVhX)-YAqv?qmsRT!3e^)bY(6PK^vhP2)Nq&Pb z315dkixxFH3PIpI@LH>}Q2DVhU#}ObC$o2bRj*3ub=ctAcObD7EXPgqyh?^F4iffh z30NEr;md$E?t>l0SZk4kL~FQZ!2d_#ox_S>#tA=CS5r!DX19NJyZ1?Y!xb5X`pTqt zxz7BCdqj1csmia0d_hLrkZsj>lG`z#FCjh_B9-*`Do<5q2vuu;ahb!RF(w$i?*6j? z1)6iw1|5YS-R0kS2X9N7qc5=*G)T2!cEI)0ufDPR!3C|XPjN54g$j_`XtgJO$^e_> z^+#Mz>q7=`H+v z7i?76?5IO&quaVA|I{uZ_&2&9oanELuzt}cYpjxLJTB2jUt+EEH|qS2dh$MN=dZ;^ zQAb@H(x)FEu1tOHuVoAAi$P?K%&fLBRZxrdoSDwm<5U@km>MwHKYDzM4zghCe zgdVnL339{MNE_qFq|{^Z5@e>kT(ptG^nj5$GC}yjt8Wa3P@tG3$ zXO&CeY!bmHacq}$3sF9!Lab!{29{+U29BEp#Y!?9pQ~m=r2mvr;RPSxIihB^{W%Te zVad%&<`6b_?^BTwn7}Wy3}m@_vReu{--WdWySd-w!tQc=WBvZY3DiTK);m-G7mgVC z`D%%*#z)h6qrJM#3-ZMucz7!2UdN$Fc#KWL8uOSs{cLl(&l84>ycH%4pbfKKV%uAb z>dEz~&T@}hD{)cck!1B5ekxaKhs}ZyM77lOSfq~Y2zV$NDBtHRn1Z?%{OLjsEYZ$>uWRZRTR=$SJvLt31gQ%cT9hda}xrn;#`-Yz7> z5c}nP8Yjxkf}t;5MJpZ^=G0fc8PLsrzv^d}!7y1fIM_%Ti6mEL=IMZsa$+}Eljxf?>#-ugv;@@RmVUk)}UOy z^?*H+Shc|(vTDpWB_Sl6Y8oc20fa-AhKdeYJ*NydJvDg3VWIKVeyzVMDakdAf8vri zG_7gBO&(}aYB#gC2@dZwrHt|Zt#bcLeG-Q8TBCH&f{vISrVw8i1ocO+72vF$r&9V~ zR0l7V1+dH+{4leTeODb)6(u@SR|ZUVLp!yw?7tQNGV3Q4_?stIyT9IeGN8Rv057_i z#pav$bM=nd9;*M+(^?+7wAreMALssnpKHTQI^a8LV!L#!68EX zwFBEg6QXC(QG&>`?^WO0ZM-HZbsT4xbBz^16>OyrOKt2XjL3|m7phhTKi&XhDl7A~ z9il95OLA7V?v?Kg{V|rh*;jb)FwDk&Wu{t!^EJ8io%)p0##F_eIh{~VVc4#by8TV^ zWC}iSzz5Zw3*6QtndO`gHVJmh9=&mo$Q@m$V4l&C+1r5a&qzxoW*M*EiF&N^yY4eR z@C^C=$Wpwo*1dqsBQ$$MF&rR&&L|oaJ5*mQP~Vghmo>(Qy_H&5#+AS1{b!iWn;K&# zwV97=ECpI1Xg)h+2zSG!;?PaAJ?NE^%OkbIz?7Q6zFHH%W=25D%`z|!$n^7^ej)tX zt-l8H)&5FV_w6+`qu)O+;(7S_j;jM>V@T=dm68~`hSi_i=o)S%u%)D~7(Ho!nC$Sc zyI~c?{>kkC2=5CWd zZ9c4;c=b*j%TifQQ$O3rlj!GC^h zv?qUTlOFh&(Wb&`vO4J%a;v@!h{?UY>7u5lYLeKeT|W!aoVm;mnt_F1t?dYQt14*@mQT0%0;tyEM0p+| zV5QI-{k$&mBJ~2bT;$F%{)wQz6&}GU#^}b5SNz5@GN z_a0|$G^P{nbD9t}ri0>iD%z?ia2>R)&+biyE9cjSAF3y(?@=SfzwA`aVag+($nDG@ zonNbRw4+Zco0+OE1oqCCXGZ8mRBrrkFWBRBXwV7Es2t-Osm26cb5aCn1mw`S?^y*95!L6N}sxd9x|OU>(aNl zV!}G&4K0?uCcOWqg54T(Pnd;c3r|k798iZ0)DgCLqp!aBYQvdRwAkh2HJc*8ByzU{ zReUNie9htSWrCDr$=??dIEI5~9r|3zuGg!&=xneOZ9H%s|A0PZ4Pxm0bnkwKx&VT- zhYOjs1fQkNGKMbWe30?}l}h`@4oNTd&mX)S*s8twU+(uy<*Tp*MY*wkL z_^>)U_SXn(``8uL=oe z-r^Ah_aJrDjZlmM4$t0A~l#0VegGU0;HU2G*dW=6KwZmLe+JEbh^g5Wabx4}- zcdTfnz?B8MB;ob$LpjqlDu|@S$>GY%X03qQ%);|5$xty~Se1*=qp>36wE9t?#@)I@ za)tZ1_2oZ{dl0Weq0#(85Ym6_s!%2vBOO`b#jU@wA^fbRn4t}ns=aEARzTJrfsHy| zeToP%3edd}lU`(oUf&UQX$FwP7hONrL$$!Zw@_a|E!q}KVy~ra=4|i}U*e;`6ydy3 z>BR!$5?gdcv%O%#b7cH|qF0bsj&Sg8l?^e-S8)C}df+AOV$z|VJm zvyuPxW;54kBFEQBfNb(Sco6_G&< zqIc)%U7QwwFQcbd^?T_yEa1_otP<*S`%nye^A^U7J|%99pX%ctF*Si}^yjR9fa*Yf zjvXG6jIkvKkOr>)U@H4Khz8?X07TD_ijpsR@FfwwJ0bDVn7HgB-M@MiTjeaq0?DTJ z_*2}1pV{Ju<#fig%S~pJ;iSQEWrwN-d2{nnDTHL?aJ9=i^~X>Qb{7LS;iC08x5X8s zOINOm;GhUBb|1E?Khg)UEG+fB!4Bf%*=&6!FkbAQ0l1c|#gdg*jD3*<;~eXOT~7Bp zfCbl1RgQbVPZGR**pj*IY)8KQY~0&D>PtJG*tL>h5idd6)w1>HY^QBt(4DqrrFqBJ z`D++Jc-*8B>Y43K^By=|wR}B>1^`>KO;r9;snyvk3VSA7tBTbyx$8m}_f3dV)14B? z3(8H5|2Sm60+^O1*H@>a)4Yu}R*Dij44kYM+>cKEbGqMk&PsYm_!$ZxT+8V$2C7#$ z`F2c3-X`Jc3y~ZHufNpir{lePo^R_g2~Wh65g4 zAz36>6+#X}%0yaL>w&Pg53N2XbHnakGngRJqCYRGah@SOzuOHu;(43A2WXd4&C%ga0&L*5ZQU?OhP z#J<^(@8v?@0L&BL|LC_DJgQ+vu}3R`hd)G!oII*@`*6>j_CTH(U)EgS?qe=cryG2l z?*p_wsjjFN_P~JGK9>o9yue8xD?qK_dlWMobDtLAE>-&Cw88f^bWhcMWqvP!NHm((an`nZ9)0B~4Q{|zJnTAJ@EPBW&{yu%%rf1xGK+mOC>0Ep6JDBjEB zeaWc@i&t}q;j^fA=A*2Ub^8SMWM-TPfoCQZZjoU&1zOcvjd$6w&)jJ&!o4g2vV3;} z-p%^7l>l;KbK!k7mWWD^$r|pVfotqcd{f-l>VVXPeB~PD3<1dAJOf5+K#%QDYYvdw zg%9)$;7-LEvhRP3*|0b|+^4zA+#<4GDG;){ip!uLEB)U0}rCa27dT@okt4!>%alC1z9<|F%$MgGlL z(S~&Q_}e{#ZmRV2NriQ8tfg$7p9-7aa;d)Gj7Gzj4)e^FtecfXRLbCS&YXX1L&7 zlycpM+WB0^J8yt#Wqk8QX<7u#yY6-l5{=Sb-?DgV$Umt7RX;DT|HD_|e!DyCSxo%p%L=<}u<)uz>_5IS zYe}Cfoa~Be;ckxj^>*y4ug4qQDNYG3gqY|3^S>BJyq?PId3yx+9(@!;-A@#EmQ>}N z*+$MGO?tf)^4s|odqKCyUS6X@L+huS|FJQ88bP|ZvZDj55z>1U44kb;kqmV0 zP*PB@Bz)Sl1Qw6jqv*J~n^$~nRdp`Zq)VgoY~bEUj(EDq%a@&s2WfodW0q|8pz-nC z30so&;m%bS>0HY&U_23U&U0d)rx=c;;IXiE2mET=;MK^$| zzZ`x89`5ZC?xW-Nzz4X1RFzfL6_nK!RMhQM)pXP~bkw!vl$CXqmDg{4xBNc@g716# z-$VW11# - - - -Created by potrace 1.14, written by Peter Selinger 2001-2017 - - - - - - diff --git a/website/static/site.webmanifest b/website/static/site.webmanifest deleted file mode 100644 index 8c1b619..0000000 --- a/website/static/site.webmanifest +++ /dev/null @@ -1,19 +0,0 @@ -{ - "name": "pesde", - "short_name": "pesde", - "icons": [ - { - "src": "/android-chrome-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "/android-chrome-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ], - "theme_color": "#f8e4d5", - "background_color": "#13100F", - "display": "standalone" -} \ No newline at end of file diff --git a/website/svelte.config.js b/website/svelte.config.js deleted file mode 100644 index a5b5a02..0000000 --- a/website/svelte.config.js +++ /dev/null @@ -1,13 +0,0 @@ -import adapter from '@sveltejs/adapter-auto'; -import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; - -/** @type {import('@sveltejs/kit').Config} */ -const config = { - preprocess: vitePreprocess(), - - kit: { - adapter: adapter() - } -}; - -export default config; diff --git a/website/tailwind.config.js b/website/tailwind.config.js deleted file mode 100644 index 116b89e..0000000 --- a/website/tailwind.config.js +++ /dev/null @@ -1,41 +0,0 @@ -import defaultTheme from 'tailwindcss/defaultTheme'; - -/** @type {import('tailwindcss').Config} */ -export default { - content: ['./src/**/*.{html,js,svelte,ts}'], - theme: { - extend: { - colors: { - 'standard-text': '#f8e4d5', - 'main-background': '#13100F', - 'paper-1': '#422911', - 'paper-1-alt': '#4C3C2D', - links: '#ffa360' - }, - fontFamily: { - serif: ['Hepta Slab Variable', defaultTheme.fontFamily.serif] - }, - typography: ({ theme }) => ({ - pesde: { - css: { - '--tw-prose-body': theme('colors.standard-text'), - '--tw-prose-headings': theme('colors.standard-text'), - '--tw-prose-lead': theme('colors.orange[100]'), - '--tw-prose-links': theme('colors.links'), - '--tw-prose-bold': theme('colors.orange[400]'), - '--tw-prose-counters': theme('colors.orange[300]'), - '--tw-prose-bullets': theme('colors.orange[300]'), - '--tw-prose-hr': theme('colors.orange[100]'), - '--tw-prose-quotes': theme('colors.orange[300]'), - '--tw-prose-quote-borders': theme('colors.orange[500]'), - '--tw-prose-captions': theme('colors.orange[300]'), - '--tw-prose-th-borders': theme('colors.orange[300]'), - '--tw-prose-td-borders': theme('colors.orange[300]'), - '--tw-prose-code': theme('colors.orange[300]') - } - } - }) - } - }, - plugins: [require('@tailwindcss/typography')] -}; diff --git a/website/tsconfig.json b/website/tsconfig.json deleted file mode 100644 index cf31bef..0000000 --- a/website/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "extends": "./.svelte-kit/tsconfig.json", - "compilerOptions": { - "allowJs": true, - "checkJs": true, - "esModuleInterop": true, - "forceConsistentCasingInFileNames": true, - "resolveJsonModule": true, - "skipLibCheck": true, - "sourceMap": true, - "strict": true, - "moduleResolution": "bundler" - } -} \ No newline at end of file diff --git a/website/vite.config.ts b/website/vite.config.ts deleted file mode 100644 index bbf8c7d..0000000 --- a/website/vite.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { sveltekit } from '@sveltejs/kit/vite'; -import { defineConfig } from 'vite'; - -export default defineConfig({ - plugins: [sveltekit()] -});
-
-
- - pesde - -
- - -
-
- -
-
-
{item?.name}
- {#if item?.description} -
- {item.description} -
- {/if} -
-
-
-
- -
- -
-