mirror of
https://github.com/pesde-pkg/pesde.git
synced 2024-12-13 11:50:36 +00:00
feat(cli): ✨ add init, add, remove, and outdated commands
This commit is contained in:
parent
f2758c6351
commit
0be8a520c3
12 changed files with 476 additions and 117 deletions
131
Cargo.lock
generated
131
Cargo.lock
generated
|
@ -509,9 +509,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "async-io"
|
name = "async-io"
|
||||||
version = "2.3.1"
|
version = "2.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f97ab0c5b00a7cdbe5a371b9a782ee7be1316095885c8a4ea1daf490eb0ef65"
|
checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-lock 3.3.0",
|
"async-lock 3.3.0",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -580,7 +580,7 @@ version = "0.2.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
|
checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-io 2.3.1",
|
"async-io 2.3.2",
|
||||||
"async-lock 2.8.0",
|
"async-lock 2.8.0",
|
||||||
"atomic-waker",
|
"atomic-waker",
|
||||||
"cfg-if",
|
"cfg-if",
|
||||||
|
@ -790,9 +790,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bumpalo"
|
name = "bumpalo"
|
||||||
version = "3.15.3"
|
version = "3.15.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8ea184aa71bb362a1157c896979544cc23974e08fd265f29ea96b59f0b4a555b"
|
checksum = "7ff69b9dd49fd426c69a0db9fc04dd934cdb6645ff000864d98f7e2af8830eaa"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytecount"
|
name = "bytecount"
|
||||||
|
@ -829,9 +829,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "cc"
|
name = "cc"
|
||||||
version = "1.0.89"
|
version = "1.0.90"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a0ba8f7aaa012f30d5b2861462f6708eccd49c3c39863fe083a308035f63d723"
|
checksum = "8cd6604a82acf3039f1144f54b8eb34e91ffba622051189e71b781822d5ee1f5"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"jobserver",
|
"jobserver",
|
||||||
"libc",
|
"libc",
|
||||||
|
@ -1077,6 +1077,31 @@ version = "0.8.19"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm"
|
||||||
|
version = "0.25.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 1.3.2",
|
||||||
|
"crossterm_winapi",
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"parking_lot",
|
||||||
|
"signal-hook",
|
||||||
|
"signal-hook-mio",
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossterm_winapi"
|
||||||
|
version = "0.9.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
|
||||||
|
dependencies = [
|
||||||
|
"winapi",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crunchy"
|
name = "crunchy"
|
||||||
version = "0.2.2"
|
version = "0.2.2"
|
||||||
|
@ -1280,6 +1305,12 @@ version = "1.0.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dyn-clone"
|
||||||
|
version = "1.0.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
|
@ -1349,9 +1380,9 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "erased-serde"
|
name = "erased-serde"
|
||||||
version = "0.4.3"
|
version = "0.4.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "388979d208a049ffdfb22fa33b9c81942215b940910bccfe258caeb25d125cb3"
|
checksum = "2b73807008a3c7f171cc40312f37d95ef0396e048b5848d775f54b1a4dd4a0d3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
@ -1689,6 +1720,24 @@ dependencies = [
|
||||||
"slab",
|
"slab",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fuzzy-matcher"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "54614a3312934d066701a80f20f15fa3b56d67ac7722b39eea5b4c9dd1d66c94"
|
||||||
|
dependencies = [
|
||||||
|
"thread_local",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fxhash"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder 1.5.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "generator"
|
name = "generator"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
|
@ -2119,6 +2168,23 @@ dependencies = [
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "inquire"
|
||||||
|
version = "0.7.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bd05e4e63529f3c9c5f5c668c398217f72756ffe48c85266b49692c55accd1f7"
|
||||||
|
dependencies = [
|
||||||
|
"bitflags 2.4.2",
|
||||||
|
"crossterm",
|
||||||
|
"dyn-clone",
|
||||||
|
"fuzzy-matcher",
|
||||||
|
"fxhash",
|
||||||
|
"newline-converter",
|
||||||
|
"once_cell",
|
||||||
|
"unicode-segmentation",
|
||||||
|
"unicode-width",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "instant"
|
name = "instant"
|
||||||
version = "0.1.12"
|
version = "0.1.12"
|
||||||
|
@ -2664,6 +2730,15 @@ dependencies = [
|
||||||
"tempfile",
|
"tempfile",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "newline-converter"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "47b6b097ecb1cbfed438542d16e84fd7ad9b0c76c8a65b7f9039212a3d14dc7f"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-segmentation",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nibble_vec"
|
name = "nibble_vec"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
@ -3034,6 +3109,7 @@ dependencies = [
|
||||||
"ignore",
|
"ignore",
|
||||||
"indicatif",
|
"indicatif",
|
||||||
"indicatif-log-bridge",
|
"indicatif-log-bridge",
|
||||||
|
"inquire",
|
||||||
"keyring",
|
"keyring",
|
||||||
"log",
|
"log",
|
||||||
"lune",
|
"lune",
|
||||||
|
@ -3053,7 +3129,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pesde-registry"
|
name = "pesde-registry"
|
||||||
version = "0.2.0"
|
version = "0.3.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-cors",
|
"actix-cors",
|
||||||
"actix-governor",
|
"actix-governor",
|
||||||
|
@ -3551,9 +3627,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.11.24"
|
version = "0.11.25"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "c6920094eb85afde5e4a138be3f2de8bbdf28000f0029e72c45025a56b042251"
|
checksum = "0eea5a9eb898d3783f17c6407670e3592fd174cb81a10e51d4c37f49450b9946"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.21.7",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -4183,6 +4259,27 @@ version = "1.1.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
checksum = "24188a676b6ae68c3b2cb3a01be17fbf7240ce009799bb56d5b1409051e78fde"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook"
|
||||||
|
version = "0.3.17"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"signal-hook-registry",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "signal-hook-mio"
|
||||||
|
version = "0.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"mio",
|
||||||
|
"signal-hook",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "signal-hook-registry"
|
name = "signal-hook-registry"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
|
@ -4391,20 +4488,20 @@ checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
|
checksum = "658bc6ee10a9b4fcf576e9b0819d95ec16f4d2c02d39fd83ac1c8789785c4a42"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags 1.3.2",
|
"bitflags 2.4.2",
|
||||||
"core-foundation",
|
"core-foundation",
|
||||||
"system-configuration-sys",
|
"system-configuration-sys",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration-sys"
|
name = "system-configuration-sys"
|
||||||
version = "0.5.0"
|
version = "0.6.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
|
checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"core-foundation-sys",
|
"core-foundation-sys",
|
||||||
"libc",
|
"libc",
|
||||||
|
|
|
@ -9,7 +9,7 @@ homepage = "https://pesde.daimond113.com"
|
||||||
include = ["src/**/*", "Cargo.toml", "Cargo.lock", "README.md", "LICENSE", "CHANGELOG.md"]
|
include = ["src/**/*", "Cargo.toml", "Cargo.lock", "README.md", "LICENSE", "CHANGELOG.md"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
bin = ["clap", "directories", "keyring", "anyhow", "ignore", "pretty_env_logger", "serde_json", "reqwest/json", "reqwest/multipart", "lune", "futures-executor", "indicatif", "auth-git2", "indicatif-log-bridge"]
|
bin = ["clap", "directories", "keyring", "anyhow", "ignore", "pretty_env_logger", "serde_json", "reqwest/json", "reqwest/multipart", "lune", "futures-executor", "indicatif", "auth-git2", "indicatif-log-bridge", "inquire"]
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
name = "pesde"
|
name = "pesde"
|
||||||
|
@ -46,6 +46,7 @@ futures-executor = { version = "0.3.30", optional = true }
|
||||||
indicatif = { version = "0.17.8", optional = true }
|
indicatif = { version = "0.17.8", optional = true }
|
||||||
auth-git2 = { version = "0.5.3", optional = true }
|
auth-git2 = { version = "0.5.3", optional = true }
|
||||||
indicatif-log-bridge = { version = "0.2.2", optional = true }
|
indicatif-log-bridge = { version = "0.2.2", optional = true }
|
||||||
|
inquire = { version = "0.7.0", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tempfile = "3.10.1"
|
tempfile = "3.10.1"
|
||||||
|
|
|
@ -17,10 +17,10 @@ pub fn auth_command(cmd: AuthCommand, params: CliParams) -> anyhow::Result<()> {
|
||||||
|
|
||||||
match cmd {
|
match cmd {
|
||||||
AuthCommand::Login => {
|
AuthCommand::Login => {
|
||||||
let response = send_request!(params.reqwest_client.post(Url::parse_with_params(
|
let response = send_request(params.reqwest_client.post(Url::parse_with_params(
|
||||||
"https://github.com/login/device/code",
|
"https://github.com/login/device/code",
|
||||||
&[("client_id", index_config.github_oauth_client_id.as_str())],
|
&[("client_id", index_config.github_oauth_client_id.as_str())],
|
||||||
)?))
|
)?))?
|
||||||
.json::<serde_json::Value>()?;
|
.json::<serde_json::Value>()?;
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
@ -43,14 +43,14 @@ pub fn auth_command(cmd: AuthCommand, params: CliParams) -> anyhow::Result<()> {
|
||||||
while time_left > 0 {
|
while time_left > 0 {
|
||||||
std::thread::sleep(interval);
|
std::thread::sleep(interval);
|
||||||
time_left -= interval.as_secs() as i64;
|
time_left -= interval.as_secs() as i64;
|
||||||
let response = send_request!(params.reqwest_client.post(Url::parse_with_params(
|
let response = send_request(params.reqwest_client.post(Url::parse_with_params(
|
||||||
"https://github.com/login/oauth/access_token",
|
"https://github.com/login/oauth/access_token",
|
||||||
&[
|
&[
|
||||||
("client_id", index_config.github_oauth_client_id.as_str()),
|
("client_id", index_config.github_oauth_client_id.as_str()),
|
||||||
("device_code", device_code),
|
("device_code", device_code),
|
||||||
("grant_type", "urn:ietf:params:oauth:grant-type:device_code")
|
("grant_type", "urn:ietf:params:oauth:grant-type:device_code"),
|
||||||
],
|
],
|
||||||
)?))
|
)?))?
|
||||||
.json::<serde_json::Value>()?;
|
.json::<serde_json::Value>()?;
|
||||||
|
|
||||||
match response
|
match response
|
||||||
|
@ -82,10 +82,12 @@ pub fn auth_command(cmd: AuthCommand, params: CliParams) -> anyhow::Result<()> {
|
||||||
|
|
||||||
params.api_token_entry.set_password(access_token)?;
|
params.api_token_entry.set_password(access_token)?;
|
||||||
|
|
||||||
let response = send_request!(params
|
let response = send_request(
|
||||||
.reqwest_client
|
params
|
||||||
.get("https://api.github.com/user")
|
.reqwest_client
|
||||||
.header(AUTHORIZATION, format!("Bearer {access_token}")))
|
.get("https://api.github.com/user")
|
||||||
|
.header(AUTHORIZATION, format!("Bearer {access_token}")),
|
||||||
|
)?
|
||||||
.json::<serde_json::Value>()?;
|
.json::<serde_json::Value>()?;
|
||||||
|
|
||||||
let login = response["login"]
|
let login = response["login"]
|
||||||
|
|
|
@ -8,6 +8,7 @@ use crate::{CliConfig, CliParams};
|
||||||
pub enum ConfigCommand {
|
pub enum ConfigCommand {
|
||||||
/// Sets the index repository URL
|
/// Sets the index repository URL
|
||||||
SetIndexRepo {
|
SetIndexRepo {
|
||||||
|
/// The URL of the index repository
|
||||||
#[clap(value_name = "URL")]
|
#[clap(value_name = "URL")]
|
||||||
url: String,
|
url: String,
|
||||||
},
|
},
|
||||||
|
@ -16,6 +17,7 @@ pub enum ConfigCommand {
|
||||||
|
|
||||||
/// Sets the cache directory
|
/// Sets the cache directory
|
||||||
SetCacheDir {
|
SetCacheDir {
|
||||||
|
/// The directory to use as the cache directory
|
||||||
#[clap(value_name = "DIRECTORY")]
|
#[clap(value_name = "DIRECTORY")]
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
},
|
},
|
||||||
|
|
240
src/cli/root.rs
240
src/cli/root.rs
|
@ -1,11 +1,13 @@
|
||||||
use std::{
|
use std::{
|
||||||
fs::{create_dir_all, read, remove_dir_all, write},
|
fs::{create_dir_all, read, remove_dir_all, write, File},
|
||||||
|
str::FromStr,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use flate2::{write::GzEncoder, Compression};
|
use flate2::{write::GzEncoder, Compression};
|
||||||
use futures_executor::block_on;
|
use futures_executor::block_on;
|
||||||
use ignore::{overrides::OverrideBuilder, WalkBuilder};
|
use ignore::{overrides::OverrideBuilder, WalkBuilder};
|
||||||
|
use inquire::{validator::Validation, Select, Text};
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use lune::Runtime;
|
use lune::Runtime;
|
||||||
use reqwest::{header::AUTHORIZATION, Url};
|
use reqwest::{header::AUTHORIZATION, Url};
|
||||||
|
@ -14,13 +16,15 @@ use serde_json::Value;
|
||||||
use tar::Builder as TarBuilder;
|
use tar::Builder as TarBuilder;
|
||||||
|
|
||||||
use pesde::{
|
use pesde::{
|
||||||
dependencies::PackageRef,
|
dependencies::{registry::RegistryDependencySpecifier, DependencySpecifier, PackageRef},
|
||||||
index::{GitIndex, Index},
|
index::{GitIndex, Index},
|
||||||
manifest::Manifest,
|
manifest::{Manifest, PathStyle, Realm},
|
||||||
|
multithread::MultithreadedJob,
|
||||||
package_name::PackageName,
|
package_name::PackageName,
|
||||||
patches::{create_patch, setup_patches_repo},
|
patches::{create_patch, setup_patches_repo},
|
||||||
project::{InstallOptions, Project},
|
project::{InstallOptions, Project},
|
||||||
DEV_PACKAGES_FOLDER, IGNORED_FOLDERS, PACKAGES_FOLDER, PATCHES_FOLDER, SERVER_PACKAGES_FOLDER,
|
DEV_PACKAGES_FOLDER, IGNORED_FOLDERS, MANIFEST_FILE_NAME, PACKAGES_FOLDER, PATCHES_FOLDER,
|
||||||
|
SERVER_PACKAGES_FOLDER,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{send_request, CliParams, Command};
|
use crate::{send_request, CliParams, Command};
|
||||||
|
@ -37,6 +41,32 @@ fn get_project(params: &CliParams) -> anyhow::Result<Project<GitIndex>> {
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn multithreaded_bar<E: Send + Sync + Into<anyhow::Error> + 'static>(
|
||||||
|
params: &CliParams,
|
||||||
|
job: MultithreadedJob<E>,
|
||||||
|
len: u64,
|
||||||
|
message: String,
|
||||||
|
) -> Result<(), anyhow::Error> {
|
||||||
|
let bar = params.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(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
match cmd {
|
match cmd {
|
||||||
Command::Install { locked } => {
|
Command::Install { locked } => {
|
||||||
|
@ -55,21 +85,13 @@ pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
let resolved_versions_map = project.manifest().dependency_tree(&project, locked)?;
|
let resolved_versions_map = project.manifest().dependency_tree(&project, locked)?;
|
||||||
|
|
||||||
let download_job = project.download(&resolved_versions_map)?;
|
let download_job = project.download(&resolved_versions_map)?;
|
||||||
let bar = params.multi.add(
|
|
||||||
indicatif::ProgressBar::new(resolved_versions_map.len() as u64)
|
|
||||||
.with_style(indicatif::ProgressStyle::default_bar().template(
|
|
||||||
"{msg} {bar:40.208/166} {pos}/{len} {percent}% {elapsed_precise}",
|
|
||||||
)?)
|
|
||||||
.with_message("Downloading packages"),
|
|
||||||
);
|
|
||||||
bar.enable_steady_tick(Duration::from_millis(100));
|
|
||||||
|
|
||||||
while let Ok(result) = download_job.progress().recv() {
|
multithreaded_bar(
|
||||||
result?;
|
¶ms,
|
||||||
bar.inc(1);
|
download_job,
|
||||||
}
|
resolved_versions_map.len() as u64,
|
||||||
|
"Downloading packages".to_string(),
|
||||||
bar.finish_with_message("done");
|
)?;
|
||||||
|
|
||||||
project.install(
|
project.install(
|
||||||
InstallOptions::new()
|
InstallOptions::new()
|
||||||
|
@ -81,16 +103,16 @@ pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
Command::Run { package, args } => {
|
Command::Run { package, args } => {
|
||||||
let project = get_project(¶ms)?;
|
let project = get_project(¶ms)?;
|
||||||
|
|
||||||
let name: PackageName = package.parse()?;
|
|
||||||
|
|
||||||
let lockfile = project
|
let lockfile = project
|
||||||
.lockfile()?
|
.lockfile()?
|
||||||
.ok_or(anyhow::anyhow!("lockfile not found"))?;
|
.ok_or(anyhow::anyhow!("lockfile not found"))?;
|
||||||
|
|
||||||
let (_, resolved_pkg) = lockfile
|
let (_, resolved_pkg) = lockfile
|
||||||
.get(&name)
|
.get(&package)
|
||||||
.and_then(|versions| versions.iter().find(|(_, pkg_ref)| pkg_ref.is_root))
|
.and_then(|versions| versions.iter().find(|(_, pkg_ref)| pkg_ref.is_root))
|
||||||
.ok_or(anyhow::anyhow!("package not found in lockfile"))?;
|
.ok_or(anyhow::anyhow!(
|
||||||
|
"package not found in lockfile (or isn't root)"
|
||||||
|
))?;
|
||||||
|
|
||||||
if !resolved_pkg.is_root {
|
if !resolved_pkg.is_root {
|
||||||
anyhow::bail!("package is not a root package");
|
anyhow::bail!("package is not a root package");
|
||||||
|
@ -116,10 +138,10 @@ pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
let config = params.index.config()?;
|
let config = params.index.config()?;
|
||||||
let api_url = config.api();
|
let api_url = config.api();
|
||||||
|
|
||||||
let response = send_request!(params.reqwest_client.get(Url::parse_with_params(
|
let response = send_request(params.reqwest_client.get(Url::parse_with_params(
|
||||||
&format!("{}/v0/search", api_url),
|
&format!("{}/v0/search", api_url),
|
||||||
&query.map_or_else(Vec::new, |q| vec![("query", q)])
|
&query.map_or_else(Vec::new, |q| vec![("query", q)]),
|
||||||
)?))
|
)?))?
|
||||||
.json::<Value>()?;
|
.json::<Value>()?;
|
||||||
|
|
||||||
for package in response.as_array().unwrap() {
|
for package in response.as_array().unwrap() {
|
||||||
|
@ -201,30 +223,24 @@ pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
request = request.header(AUTHORIZATION, "");
|
request = request.header(AUTHORIZATION, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
println!("{}", send_request!(request).text()?);
|
println!("{}", send_request(request)?.text()?);
|
||||||
}
|
}
|
||||||
Command::Patch { package } => {
|
Command::Patch { package } => {
|
||||||
let project = get_project(¶ms)?;
|
let project = get_project(¶ms)?;
|
||||||
|
|
||||||
let (name, version) = package
|
|
||||||
.split_once('@')
|
|
||||||
.ok_or(anyhow::anyhow!("Malformed package name"))?;
|
|
||||||
let name: PackageName = name.parse()?;
|
|
||||||
let version = Version::parse(version)?;
|
|
||||||
|
|
||||||
let lockfile = project
|
let lockfile = project
|
||||||
.lockfile()?
|
.lockfile()?
|
||||||
.ok_or(anyhow::anyhow!("lockfile not found"))?;
|
.ok_or(anyhow::anyhow!("lockfile not found"))?;
|
||||||
|
|
||||||
let resolved_pkg = lockfile
|
let resolved_pkg = lockfile
|
||||||
.get(&name)
|
.get(&package.0)
|
||||||
.and_then(|versions| versions.get(&version))
|
.and_then(|versions| versions.get(&package.1))
|
||||||
.ok_or(anyhow::anyhow!("package not found in lockfile"))?;
|
.ok_or(anyhow::anyhow!("package not found in lockfile"))?;
|
||||||
|
|
||||||
let dir = params.directories.data_dir().join("patches").join(format!(
|
let dir = params.directories.data_dir().join("patches").join(format!(
|
||||||
"{}_{}",
|
"{}_{}",
|
||||||
name.escaped(),
|
package.0.escaped(),
|
||||||
version
|
package.1
|
||||||
));
|
));
|
||||||
|
|
||||||
if dir.exists() {
|
if dir.exists() {
|
||||||
|
@ -274,6 +290,158 @@ pub fn root_command(cmd: Command, params: CliParams) -> anyhow::Result<()> {
|
||||||
env!("CARGO_BIN_NAME")
|
env!("CARGO_BIN_NAME")
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
Command::Init => {
|
||||||
|
let manifest_path = params.cwd.join(MANIFEST_FILE_NAME);
|
||||||
|
|
||||||
|
if manifest_path.exists() {
|
||||||
|
anyhow::bail!("manifest already exists");
|
||||||
|
}
|
||||||
|
|
||||||
|
let default_name = params.cwd.file_name().unwrap().to_str().unwrap();
|
||||||
|
|
||||||
|
let name = Text::new("What is the name of the package?")
|
||||||
|
.with_initial_value(default_name)
|
||||||
|
.with_validator(|name: &str| {
|
||||||
|
Ok(match PackageName::from_str(name) {
|
||||||
|
Ok(_) => Validation::Valid,
|
||||||
|
Err(e) => Validation::Invalid(e.into()),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.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())
|
||||||
|
.collect::<Vec<String>>();
|
||||||
|
|
||||||
|
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),
|
||||||
|
dependencies: Default::default(),
|
||||||
|
peer_dependencies: Default::default(),
|
||||||
|
description: Some(description),
|
||||||
|
license: Some(license),
|
||||||
|
authors: Some(authors),
|
||||||
|
};
|
||||||
|
|
||||||
|
serde_yaml::to_writer(File::create(manifest_path)?, &manifest)?;
|
||||||
|
}
|
||||||
|
Command::Add {
|
||||||
|
package,
|
||||||
|
realm,
|
||||||
|
peer,
|
||||||
|
} => {
|
||||||
|
let project = get_project(¶ms)?;
|
||||||
|
|
||||||
|
let mut manifest = project.manifest().clone();
|
||||||
|
|
||||||
|
let specifier = DependencySpecifier::Registry(RegistryDependencySpecifier {
|
||||||
|
name: package.0,
|
||||||
|
version: package.1,
|
||||||
|
realm,
|
||||||
|
});
|
||||||
|
|
||||||
|
if peer {
|
||||||
|
manifest.peer_dependencies.push(specifier);
|
||||||
|
} else {
|
||||||
|
manifest.dependencies.push(specifier);
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_yaml::to_writer(
|
||||||
|
File::create(project.path().join(MANIFEST_FILE_NAME))?,
|
||||||
|
&manifest,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Command::Remove { package } => {
|
||||||
|
let project = get_project(¶ms)?;
|
||||||
|
|
||||||
|
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 {
|
||||||
|
registry.name != package
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
serde_yaml::to_writer(
|
||||||
|
File::create(project.path().join(MANIFEST_FILE_NAME))?,
|
||||||
|
&manifest,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
Command::Outdated => {
|
||||||
|
let project = get_project(¶ms)?;
|
||||||
|
|
||||||
|
let manifest = project.manifest();
|
||||||
|
let dependency_tree = manifest.dependency_tree(&project, false)?;
|
||||||
|
|
||||||
|
for (name, versions) in dependency_tree {
|
||||||
|
for (version, resolved_pkg) in versions {
|
||||||
|
if !resolved_pkg.is_root {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let PackageRef::Registry(registry) = resolved_pkg.pkg_ref {
|
||||||
|
let latest_version = send_request(params.reqwest_client.get(format!(
|
||||||
|
"{}/v0/packages/{}/{}/versions",
|
||||||
|
project.index().config()?.api(),
|
||||||
|
registry.name.scope(),
|
||||||
|
registry.name.name()
|
||||||
|
)))?
|
||||||
|
.json::<Value>()?
|
||||||
|
.as_array()
|
||||||
|
.unwrap()
|
||||||
|
.iter()
|
||||||
|
.map(|v| Version::parse(v.as_str().unwrap()))
|
||||||
|
.collect::<Result<Vec<Version>, semver::Error>>()?
|
||||||
|
.into_iter()
|
||||||
|
.max()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
if latest_version > version {
|
||||||
|
println!(
|
||||||
|
"{name}@{version} is outdated. latest version: {latest_version}"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct GitDependencySpecifier {
|
||||||
/// The revision of the git repository to use
|
/// The revision of the git repository to use
|
||||||
pub rev: String,
|
pub rev: String,
|
||||||
/// The realm of the package
|
/// The realm of the package
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub realm: Option<Realm>,
|
pub realm: Option<Realm>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,17 +66,15 @@ impl GitDependencySpecifier {
|
||||||
// should also work with ssh urls
|
// should also work with ssh urls
|
||||||
let is_url = self.repo.contains(':');
|
let is_url = self.repo.contains(':');
|
||||||
|
|
||||||
let repo_name = {
|
let repo_name = if !is_url {
|
||||||
if !is_url {
|
self.repo.to_string()
|
||||||
self.repo.to_string()
|
} else {
|
||||||
} else {
|
let parts: Vec<&str> = self.repo.split('/').collect();
|
||||||
let parts: Vec<&str> = self.repo.split('/').collect();
|
format!(
|
||||||
format!(
|
"{}/{}",
|
||||||
"{}/{}",
|
parts[parts.len() - 2],
|
||||||
parts[parts.len() - 2],
|
parts[parts.len() - 1].trim_end_matches(".git")
|
||||||
parts[parts.len() - 1].trim_end_matches(".git")
|
)
|
||||||
)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_url {
|
if is_url {
|
||||||
|
@ -84,12 +83,10 @@ impl GitDependencySpecifier {
|
||||||
debug!("assuming git repository is a name: {}", &repo_name);
|
debug!("assuming git repository is a name: {}", &repo_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo_url = {
|
let repo_url = if !is_url {
|
||||||
if !is_url {
|
format!("https://github.com/{}.git", &self.repo)
|
||||||
format!("https://github.com/{}.git", &self.repo)
|
} else {
|
||||||
} else {
|
self.repo.to_string()
|
||||||
self.repo.to_string()
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if is_url {
|
if is_url {
|
||||||
|
|
|
@ -143,7 +143,7 @@ impl<I: Index> Project<I> {
|
||||||
&self,
|
&self,
|
||||||
map: &ResolvedVersionsMap,
|
map: &ResolvedVersionsMap,
|
||||||
) -> Result<MultithreadedJob<DownloadError>, InstallProjectError> {
|
) -> Result<MultithreadedJob<DownloadError>, InstallProjectError> {
|
||||||
let (job, tx) = MultithreadedJob::new();
|
let job = MultithreadedJob::new();
|
||||||
|
|
||||||
for (name, versions) in map.clone() {
|
for (name, versions) in map.clone() {
|
||||||
for (version, resolved_package) in versions {
|
for (version, resolved_package) in versions {
|
||||||
|
@ -162,12 +162,8 @@ impl<I: Index> Project<I> {
|
||||||
create_dir_all(&source)?;
|
create_dir_all(&source)?;
|
||||||
|
|
||||||
let project = self.clone();
|
let project = self.clone();
|
||||||
let tx = tx.clone();
|
|
||||||
|
|
||||||
job.pool.execute(move || {
|
job.execute(move || resolved_package.pkg_ref.download(&project, source));
|
||||||
let result = resolved_package.pkg_ref.download(&project, source);
|
|
||||||
tx.send(result).unwrap();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ pub struct RegistryDependencySpecifier {
|
||||||
// #[serde(skip_serializing_if = "Option::is_none")]
|
// #[serde(skip_serializing_if = "Option::is_none")]
|
||||||
// pub registry: Option<String>,
|
// pub registry: Option<String>,
|
||||||
/// The realm of the package
|
/// The realm of the package
|
||||||
|
#[serde(skip_serializing_if = "Option::is_none")]
|
||||||
pub realm: Option<Realm>,
|
pub realm: Option<Realm>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -515,7 +515,6 @@ impl IndexConfig {
|
||||||
|
|
||||||
/// An entry in the index file
|
/// An entry in the index file
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
#[serde(deny_unknown_fields)]
|
|
||||||
pub struct IndexFileEntry {
|
pub struct IndexFileEntry {
|
||||||
/// The version of the package
|
/// The version of the package
|
||||||
pub version: Version,
|
pub version: Version,
|
||||||
|
@ -525,12 +524,6 @@ pub struct IndexFileEntry {
|
||||||
/// A description of the package
|
/// A description of the package
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
/// The license of the package
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub license: Option<String>,
|
|
||||||
/// The authors of the package
|
|
||||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
||||||
pub authors: Option<Vec<String>>,
|
|
||||||
|
|
||||||
/// The dependencies of the package
|
/// The dependencies of the package
|
||||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||||
|
@ -546,8 +539,6 @@ impl From<Manifest> for IndexFileEntry {
|
||||||
realm: manifest.realm,
|
realm: manifest.realm,
|
||||||
|
|
||||||
description: manifest.description,
|
description: manifest.description,
|
||||||
license: manifest.license,
|
|
||||||
authors: manifest.authors,
|
|
||||||
|
|
||||||
dependencies,
|
dependencies,
|
||||||
}
|
}
|
||||||
|
|
90
src/main.rs
90
src/main.rs
|
@ -3,6 +3,7 @@ use std::{
|
||||||
fs::{create_dir_all, read},
|
fs::{create_dir_all, read},
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use auth_git2::GitAuthenticator;
|
use auth_git2::GitAuthenticator;
|
||||||
|
@ -11,8 +12,13 @@ use directories::ProjectDirs;
|
||||||
use indicatif::MultiProgress;
|
use indicatif::MultiProgress;
|
||||||
use indicatif_log_bridge::LogWrapper;
|
use indicatif_log_bridge::LogWrapper;
|
||||||
use keyring::Entry;
|
use keyring::Entry;
|
||||||
|
use log::error;
|
||||||
use pretty_env_logger::env_logger::Env;
|
use pretty_env_logger::env_logger::Env;
|
||||||
use reqwest::header::{ACCEPT, AUTHORIZATION};
|
use reqwest::{
|
||||||
|
blocking::{RequestBuilder, Response},
|
||||||
|
header::{ACCEPT, AUTHORIZATION},
|
||||||
|
};
|
||||||
|
use semver::{Version, VersionReq};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use cli::{
|
use cli::{
|
||||||
|
@ -20,29 +26,79 @@ use cli::{
|
||||||
config::{config_command, ConfigCommand},
|
config::{config_command, ConfigCommand},
|
||||||
root::root_command,
|
root::root_command,
|
||||||
};
|
};
|
||||||
use pesde::index::GitIndex;
|
use pesde::{index::GitIndex, manifest::Realm, package_name::PackageName};
|
||||||
|
|
||||||
mod cli;
|
mod cli;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct VersionedPackageName<V: FromStr<Err = semver::Error>>(PackageName, V);
|
||||||
|
|
||||||
|
impl<V: FromStr<Err = semver::Error>> FromStr for VersionedPackageName<V> {
|
||||||
|
type Err = anyhow::Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
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)]
|
#[derive(Subcommand)]
|
||||||
pub enum Command {
|
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<VersionReq>,
|
||||||
|
|
||||||
|
/// Whether the package is a peer dependency
|
||||||
|
#[clap(long, short)]
|
||||||
|
peer: bool,
|
||||||
|
|
||||||
|
/// The realm of the package
|
||||||
|
#[clap(long, short)]
|
||||||
|
realm: Option<Realm>,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// 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
|
/// Installs the dependencies of the project
|
||||||
Install {
|
Install {
|
||||||
|
/// Whether to use the lockfile for resolving dependencies
|
||||||
#[clap(long, short)]
|
#[clap(long, short)]
|
||||||
locked: bool,
|
locked: bool,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Runs the `bin` export of the specified package
|
/// Runs the `bin` export of the specified package
|
||||||
Run {
|
Run {
|
||||||
|
/// The package to run
|
||||||
#[clap(value_name = "PACKAGE")]
|
#[clap(value_name = "PACKAGE")]
|
||||||
package: String,
|
package: PackageName,
|
||||||
|
|
||||||
|
/// The arguments to pass to the package
|
||||||
#[clap(last = true)]
|
#[clap(last = true)]
|
||||||
args: Vec<String>,
|
args: Vec<String>,
|
||||||
},
|
},
|
||||||
|
|
||||||
/// Searches for a package on the registry
|
/// Searches for a package on the registry
|
||||||
Search {
|
Search {
|
||||||
|
/// The query to search for
|
||||||
#[clap(value_name = "QUERY")]
|
#[clap(value_name = "QUERY")]
|
||||||
query: Option<String>,
|
query: Option<String>,
|
||||||
},
|
},
|
||||||
|
@ -51,10 +107,15 @@ pub enum Command {
|
||||||
Publish,
|
Publish,
|
||||||
|
|
||||||
/// Begins a new patch
|
/// Begins a new patch
|
||||||
Patch { package: String },
|
Patch {
|
||||||
|
/// The package to patch
|
||||||
|
#[clap(value_name = "PACKAGE")]
|
||||||
|
package: VersionedPackageName<Version>,
|
||||||
|
},
|
||||||
|
|
||||||
/// Commits (finished) the patch
|
/// Commits (finishes) the patch
|
||||||
PatchCommit {
|
PatchCommit {
|
||||||
|
/// The package's changed directory
|
||||||
#[clap(value_name = "DIRECTORY")]
|
#[clap(value_name = "DIRECTORY")]
|
||||||
dir: PathBuf,
|
dir: PathBuf,
|
||||||
},
|
},
|
||||||
|
@ -77,6 +138,7 @@ struct Cli {
|
||||||
#[clap(subcommand)]
|
#[clap(subcommand)]
|
||||||
command: Command,
|
command: Command,
|
||||||
|
|
||||||
|
/// The directory to run the command in
|
||||||
#[arg(short, long, value_name = "DIRECTORY")]
|
#[arg(short, long, value_name = "DIRECTORY")]
|
||||||
directory: Option<PathBuf>,
|
directory: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
@ -117,18 +179,16 @@ impl CliConfig {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
pub fn send_request(request_builder: RequestBuilder) -> anyhow::Result<Response> {
|
||||||
macro_rules! send_request {
|
let res = request_builder.send()?;
|
||||||
($req:expr) => {{
|
|
||||||
let res = $req.send()?;
|
|
||||||
|
|
||||||
match res.error_for_status_ref() {
|
match res.error_for_status_ref() {
|
||||||
Ok(_) => res,
|
Ok(_) => Ok(res),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
panic!("request failed: {e}\nbody: {}", res.text()?);
|
error!("request failed: {e}\nbody: {}", res.text()?);
|
||||||
}
|
Err(e.into())
|
||||||
}
|
}
|
||||||
}};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> anyhow::Result<()> {
|
fn main() -> anyhow::Result<()> {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
use std::str::FromStr;
|
||||||
use std::{collections::BTreeMap, fmt::Display, fs::read};
|
use std::{collections::BTreeMap, fmt::Display, fs::read};
|
||||||
|
|
||||||
use relative_path::RelativePathBuf;
|
use relative_path::RelativePathBuf;
|
||||||
|
@ -87,6 +88,24 @@ impl Display for Realm {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"shared" => Ok(Realm::Shared),
|
||||||
|
"server" => Ok(Realm::Server),
|
||||||
|
"development" => Ok(Realm::Development),
|
||||||
|
_ => Err(FromStrRealmError(s.to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The manifest of a package
|
/// The manifest of a package
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
// #[serde(deny_unknown_fields)]
|
// #[serde(deny_unknown_fields)]
|
||||||
|
|
|
@ -1,18 +1,30 @@
|
||||||
use std::sync::mpsc::Receiver;
|
use std::sync::mpsc::{Receiver, Sender};
|
||||||
use threadpool::ThreadPool;
|
use threadpool::ThreadPool;
|
||||||
|
|
||||||
/// A multithreaded job
|
/// A multithreaded job
|
||||||
pub struct MultithreadedJob<E> {
|
pub struct MultithreadedJob<E: Send + Sync + 'static> {
|
||||||
pub(crate) progress: Receiver<Result<(), E>>,
|
progress: Receiver<Result<(), E>>,
|
||||||
pub(crate) pool: ThreadPool,
|
sender: Sender<Result<(), E>>,
|
||||||
|
pool: ThreadPool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E> MultithreadedJob<E> {
|
impl<E: Send + Sync + 'static> Default for MultithreadedJob<E> {
|
||||||
pub(crate) fn new() -> (Self, std::sync::mpsc::Sender<Result<(), E>>) {
|
fn default() -> Self {
|
||||||
let (tx, rx) = std::sync::mpsc::channel();
|
let (tx, rx) = std::sync::mpsc::channel();
|
||||||
let pool = ThreadPool::new(6);
|
let pool = ThreadPool::new(6);
|
||||||
|
|
||||||
(Self { progress: rx, pool }, tx)
|
Self {
|
||||||
|
progress: rx,
|
||||||
|
pool,
|
||||||
|
sender: tx.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: Send + Sync + 'static> MultithreadedJob<E> {
|
||||||
|
/// Creates a new multithreaded job
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the progress of the job
|
/// Returns the progress of the job
|
||||||
|
@ -30,4 +42,17 @@ impl<E> MultithreadedJob<E> {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Executes a function on the thread pool
|
||||||
|
pub fn execute<F>(&self, f: F)
|
||||||
|
where
|
||||||
|
F: (FnOnce() -> Result<(), E>) + Send + 'static,
|
||||||
|
{
|
||||||
|
let sender = self.sender.clone();
|
||||||
|
|
||||||
|
self.pool.execute(move || {
|
||||||
|
let result = f();
|
||||||
|
sender.send(result).unwrap();
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue