feat: CLI Configuration and maintainability

This commit is contained in:
quexeky
2026-01-20 17:44:33 +11:00
parent 85b2e65b5f
commit a3cc54f8a6
17 changed files with 672 additions and 136 deletions
+2 -1
View File
@@ -1,2 +1,3 @@
/target
logs/
logs/
.vscode
+396 -4
View File
@@ -2,6 +2,21 @@
# It is not intended for manual editing.
version = 4
[[package]]
name = "addr2line"
version = "0.25.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b"
dependencies = [
"gimli",
]
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
[[package]]
name = "android_system_properties"
version = "0.1.5"
@@ -220,6 +235,21 @@ dependencies = [
"thiserror 2.0.17",
]
[[package]]
name = "backtrace"
version = "0.3.76"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6"
dependencies = [
"addr2line",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
"windows-link 0.2.1",
]
[[package]]
name = "base64"
version = "0.22.1"
@@ -354,6 +384,33 @@ dependencies = [
"cc",
]
[[package]]
name = "color-eyre"
version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d"
dependencies = [
"backtrace",
"color-spantrace",
"eyre",
"indenter",
"once_cell",
"owo-colors",
"tracing-error",
]
[[package]]
name = "color-spantrace"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427"
dependencies = [
"once_cell",
"owo-colors",
"tracing-core",
"tracing-error",
]
[[package]]
name = "colorchoice"
version = "1.0.4"
@@ -426,6 +483,15 @@ dependencies = [
"tiny-keccak",
]
[[package]]
name = "convert_case"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -461,6 +527,33 @@ dependencies = [
"libc",
]
[[package]]
name = "crossterm"
version = "0.29.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b"
dependencies = [
"bitflags",
"crossterm_winapi",
"derive_more",
"document-features",
"mio",
"parking_lot",
"rustix",
"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]]
name = "crunchy"
version = "0.2.4"
@@ -521,6 +614,28 @@ dependencies = [
"serde_core",
]
[[package]]
name = "derive_more"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134"
dependencies = [
"derive_more-impl",
]
[[package]]
name = "derive_more-impl"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb"
dependencies = [
"convert_case",
"proc-macro2",
"quote",
"rustc_version",
"syn",
]
[[package]]
name = "dialoguer"
version = "0.12.0"
@@ -544,6 +659,27 @@ dependencies = [
"subtle",
]
[[package]]
name = "dirs"
version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
dependencies = [
"libc",
"option-ext",
"redox_users",
"windows-sys 0.61.2",
]
[[package]]
name = "displaydoc"
version = "0.2.5"
@@ -564,6 +700,15 @@ dependencies = [
"const-random",
]
[[package]]
name = "document-features"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
dependencies = [
"litrs",
]
[[package]]
name = "downpour"
version = "0.1.0"
@@ -572,11 +717,14 @@ dependencies = [
"async-trait",
"chrono",
"clap",
"color-eyre",
"console",
"dialoguer",
"dirs",
"droplet-rs",
"fern",
"indicatif",
"inquire",
"log",
"rand",
"reqwest 0.13.1",
@@ -655,6 +803,16 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "eyre"
version = "0.6.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec"
dependencies = [
"indenter",
"once_cell",
]
[[package]]
name = "fastrand"
version = "2.3.0"
@@ -802,6 +960,15 @@ dependencies = [
"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 = "generic-array"
version = "0.14.7"
@@ -839,6 +1006,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "gimli"
version = "0.32.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7"
[[package]]
name = "h2"
version = "0.4.13"
@@ -1154,6 +1327,12 @@ dependencies = [
"icu_properties",
]
[[package]]
name = "indenter"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5"
[[package]]
name = "indexmap"
version = "2.12.1"
@@ -1177,6 +1356,20 @@ dependencies = [
"web-time",
]
[[package]]
name = "inquire"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae51d5da01ce7039024fbdec477767c102c454dbdb09d4e2a432ece705b1b25d"
dependencies = [
"bitflags",
"crossterm",
"dyn-clone",
"fuzzy-matcher",
"unicode-segmentation",
"unicode-width",
]
[[package]]
name = "ipnet"
version = "2.11.0"
@@ -1265,6 +1458,16 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de"
[[package]]
name = "libredox"
version = "0.1.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616"
dependencies = [
"bitflags",
"libc",
]
[[package]]
name = "linux-raw-sys"
version = "0.11.0"
@@ -1277,6 +1480,21 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77"
[[package]]
name = "litrs"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
[[package]]
name = "lock_api"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.29"
@@ -1333,6 +1551,15 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]]
name = "miniz_oxide"
version = "0.8.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
dependencies = [
"adler2",
]
[[package]]
name = "mio"
version = "1.1.1"
@@ -1340,6 +1567,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc"
dependencies = [
"libc",
"log",
"wasi",
"windows-sys 0.61.2",
]
@@ -1464,6 +1692,15 @@ dependencies = [
"objc2-core-foundation",
]
[[package]]
name = "object"
version = "0.37.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe"
dependencies = [
"memchr",
]
[[package]]
name = "oid-registry"
version = "0.7.1"
@@ -1544,6 +1781,12 @@ dependencies = [
"vcpkg",
]
[[package]]
name = "option-ext"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
[[package]]
name = "ordered-multimap"
version = "0.7.3"
@@ -1554,6 +1797,35 @@ dependencies = [
"hashbrown 0.14.5",
]
[[package]]
name = "owo-colors"
version = "4.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52"
[[package]]
name = "parking_lot"
version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-link 0.2.1",
]
[[package]]
name = "pem"
version = "3.0.6"
@@ -1695,9 +1967,9 @@ dependencies = [
[[package]]
name = "quote"
version = "1.0.42"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
checksum = "dc74d9a594b72ae6656596548f56f667211f8a97b3d4c3d467150794690dc40a"
dependencies = [
"proc-macro2",
]
@@ -1751,6 +2023,26 @@ dependencies = [
"yasna",
]
[[package]]
name = "redox_syscall"
version = "0.5.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
dependencies = [
"bitflags",
]
[[package]]
name = "redox_users"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
dependencies = [
"getrandom 0.2.16",
"libredox",
"thiserror 2.0.17",
]
[[package]]
name = "reqwest"
version = "0.12.28"
@@ -1889,12 +2181,27 @@ dependencies = [
"url",
]
[[package]]
name = "rustc-demangle"
version = "0.1.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d"
[[package]]
name = "rustc-hash"
version = "2.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
[[package]]
name = "rustc_version"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
dependencies = [
"semver",
]
[[package]]
name = "rusticata-macros"
version = "4.1.0"
@@ -2041,6 +2348,12 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "security-framework"
version = "2.11.1"
@@ -2077,6 +2390,12 @@ dependencies = [
"libc",
]
[[package]]
name = "semver"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
[[package]]
name = "serde"
version = "1.0.228"
@@ -2143,6 +2462,15 @@ 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.1"
@@ -2155,6 +2483,27 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b75a19a7a740b25bc7944bdee6172368f988763b744e3d4dfe753f6b4ece40cc"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.7"
@@ -2212,9 +2561,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "syn"
version = "2.0.111"
version = "2.0.114"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
checksum = "d4d107df263a3013ef9b1879b0df87d706ff80f65a86ea879bd9c31f9b307c2a"
dependencies = [
"proc-macro2",
"quote",
@@ -2329,6 +2678,15 @@ dependencies = [
"syn",
]
[[package]]
name = "thread_local"
version = "1.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185"
dependencies = [
"cfg-if",
]
[[package]]
name = "time"
version = "0.3.44"
@@ -2527,6 +2885,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a"
dependencies = [
"once_cell",
"valuable",
]
[[package]]
name = "tracing-error"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db"
dependencies = [
"tracing",
"tracing-subscriber",
]
[[package]]
name = "tracing-subscriber"
version = "0.3.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e"
dependencies = [
"sharded-slab",
"thread_local",
"tracing-core",
]
[[package]]
@@ -2547,6 +2927,12 @@ version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
[[package]]
name = "unicode-segmentation"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493"
[[package]]
name = "unicode-width"
version = "0.2.2"
@@ -2600,6 +2986,12 @@ dependencies = [
"wasm-bindgen",
]
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
[[package]]
name = "vcpkg"
version = "0.2.15"
+3
View File
@@ -10,6 +10,7 @@ chrono = "0.4.43"
clap = { version = "4.5.54", features = ["derive"] }
console = "0.16.2"
dialoguer = "0.12.0"
dirs = "6.0.0"
droplet-rs = { git = "https://github.com/Drop-OSS/droplet-rs.git", version = "0.14" }
fern = { version = "0.7.1", features = ["colored"] }
indicatif = "0.18.3"
@@ -23,3 +24,5 @@ tokio = { version = "1.48.0", features = ["macros"] }
tokio-util = "0.7.18"
url = "2.5.8"
webbrowser = "1.0.6"
color-eyre = "0.6.5"
inquire = "0.9.2"
+1
View File
@@ -34,6 +34,7 @@
git
rust-bin.nightly.latest.default
rust-analyzer
cargo-expand
];
+5 -7
View File
@@ -2,6 +2,8 @@ use std::path::PathBuf;
use clap::{Args, Parser, Subcommand, ValueEnum};
use crate::config::config::ConfigOption;
#[derive(Parser)]
#[command(version, about, long_about = None)]
pub struct Cli {
@@ -16,16 +18,12 @@ pub struct Cli {
#[derive(Subcommand)]
pub enum Commands {
/// Configures a new Drop server
Configure {
/// Endpoint of the Drop server
url: String,
/// API token for non-interactive configuration.
#[arg(short, long)]
token: Option<String>,
},
#[command(subcommand)]
Configure(ConfigOption),
/// Uploads new game version to depot
Upload(UploadInfo),
}
#[derive(Args)]
pub struct UploadInfo {
/// Identifies the specific upload style that will be used for the set depot
+2 -5
View File
@@ -1,10 +1,7 @@
use std::path::Path;
use crate::{
cli::UploadInfo,
commands::upload::{s3::S3, uploadable::Uploadable},
config::Config,
manifest::generate_manifest,
cli::UploadInfo, commands::upload::{s3::S3, uploadable::Uploadable}, config::config::Config, manifest::generate_manifest
};
use log::info;
@@ -16,7 +13,7 @@ pub async fn upload(info: &UploadInfo, config: Config) -> anyhow::Result<()> {
let manifest = generate_manifest(&Path::new(path)).await?;
let mut uploader: Box<dyn Uploadable> = match info.upload_style {
crate::cli::UploadStyle::S3 => Box::new(S3::new(
config
&config
.get_active_s3()
.ok_or(anyhow::Error::msg("Could not get active S3 value"))?,
)?),
+1 -2
View File
@@ -1,5 +1,4 @@
pub mod interface;
pub mod s3;
pub mod speedtest;
pub mod uploadable;
pub mod void;
pub mod speedtest;
+2 -3
View File
@@ -2,12 +2,11 @@ use crate::{
commands::upload::{
speedtest::{SPEEDTEST_PATH, Speedtest},
uploadable::Uploadable,
},
config::S3Config,
}, config::s3::S3Config,
};
use async_trait::async_trait;
use droplet_rs::manifest::{ChunkData, Manifest};
use s3::{Bucket, creds::Credentials};
use s3::Bucket;
use serde_json::json;
use std::{ops::Deref, path::PathBuf};
-40
View File
@@ -1,40 +0,0 @@
use async_trait::async_trait;
use droplet_rs::manifest::{ChunkData, Manifest};
use log::warn;
use crate::commands::upload::uploadable::Uploadable;
pub struct VoidUploadable;
#[async_trait]
impl Uploadable for VoidUploadable {
async fn upload_chunk(
&mut self,
_id: &String,
_version: &String,
_chunk_id: &String,
_chunk: &ChunkData,
) -> anyhow::Result<()> {
warn!("Uploading chunk to VoidUploader");
Ok(())
}
async fn upload_speedtest(&mut self) -> anyhow::Result<()> {
warn!("Uploading speedtest to VoidUploader");
Ok(())
}
async fn upload_manifest(
&mut self,
_manifest: Manifest,
_game_id: &String,
_version_id: &String,
) -> anyhow::Result<()> {
warn!("Uploading manifest to VoidUploader");
Ok(())
}
}
impl VoidUploadable {
pub fn new() -> Self {
Self
}
}
-67
View File
@@ -1,67 +0,0 @@
use std::str::FromStr;
use s3::{Bucket, Region, creds::Credentials};
pub struct Config {
items: Vec<ConfigItem>,
active_s3: Option<String>,
}
pub struct ConfigItem {
name: String,
config_option: ConfigOption,
}
enum ConfigOption {
S3(S3Config),
}
pub struct S3Config {
secret_key: String,
key_id: String,
region: String,
bucket_name: String,
endpoint: Option<String>,
}
impl Config {
pub fn new() -> Self {
Self {
items: Vec::new(),
active_s3: None,
}
}
pub fn get_active_s3(&self) -> Option<&S3Config> {
if let Some(active_s3) = &self.active_s3 {
self.items
.iter()
.filter_map(|item| {
if item.name == *active_s3 {
match &item.config_option {
ConfigOption::S3(s3_config) => Some(s3_config),
}
} else {
None
}
})
.next()
} else {
None
}
}
}
impl S3Config {
pub fn generate_bucket(&self) -> anyhow::Result<s3::Bucket> {
let credentials =
Credentials::new(Some(&self.key_id), Some(&self.secret_key), None, None, None)?;
let region = if let Some(endpoint) = &self.endpoint {
Region::Custom {
region: self.region.clone(),
endpoint: endpoint.clone(),
}
} else {
Region::from_str(&self.region)?
};
let bucket = Bucket::new(&self.bucket_name, region, credentials)?;
Ok(*bucket)
}
}
+90
View File
@@ -0,0 +1,90 @@
use std::{fs, str::FromStr};
use clap::Subcommand;
use dialoguer::{Input, theme::ColorfulTheme};
use log::warn;
use serde::{Deserialize, Serialize};
use crate::config::{
s3::{S3Config, S3ConfigCli},
server::ServerConfig,
};
const CONFIG_DIR: &str = "downpour/config.json";
#[derive(Serialize, Deserialize)]
pub struct Config {
items: Vec<ConfigItem>,
active_s3: Option<String>,
}
impl Config {
pub fn save(&self) -> anyhow::Result<()> {
let json = serde_json::to_string(self)?;
let save_path = dirs::config_dir()
.expect("Apparently your home directory doesn't exist") // Should probably formalise that error
.join(CONFIG_DIR);
fs::write(save_path, json)?;
Ok(())
}
pub fn read() -> Self {
let save_path = dirs::config_dir()
.expect("Apparently your home directory doesn't exist") // Should probably formalise that error
.join(CONFIG_DIR);
if fs::exists(&save_path).expect(&format!("Could not read save path {:#?}", &save_path)) {
serde_json::from_str(&fs::read_to_string(save_path).unwrap()).unwrap()
} else {
Config::new()
}
}
pub fn add_item(&mut self, item: ConfigItem) {
if matches!(item.config_option, ConfigOption::S3(..)) {
self.active_s3 = Some(item.name.clone())
}
self.items.push(item);
}
}
#[derive(Serialize, Deserialize)]
pub struct ConfigItem {
name: String,
config_option: ConfigOption,
}
#[derive(Subcommand, Serialize, Deserialize)]
pub enum ConfigOption {
Server(ServerConfig),
S3(S3ConfigCli),
}
impl Config {
pub fn new() -> Self {
Self {
items: Vec::new(),
active_s3: None,
}
}
pub fn get_active_s3(&self) -> Option<S3Config> {
if let Some(active_s3) = &self.active_s3 {
self.items
.iter()
.filter_map(|item| {
if item.name == *active_s3 {
match &item.config_option {
ConfigOption::S3(s3_config) => Some(s3_config),
_ => {
warn!("Name {} is not of type 'S3'", item.name);
None
}
}
} else {
None
}
})
.next()
.cloned()
.map(|c| c.into())
} else {
None
}
}
}
+7
View File
@@ -0,0 +1,7 @@
use crate::config::config::Config;
/// Trait which represents data which may be stored in `config_dir/downpour/config.json`
pub trait Configurable {
fn configure(&self, config: &mut Config);
}
+4
View File
@@ -0,0 +1,4 @@
pub mod config;
pub mod configurable;
pub mod s3;
pub mod server;
+72
View File
@@ -0,0 +1,72 @@
use std::str::FromStr;
use clap::Args;
use dialoguer::{Input, theme::ColorfulTheme};
use s3::{Bucket, Region, creds::Credentials};
use serde::{Deserialize, Serialize};
use crate::{config::configurable::Configurable, interactive_optional_variable, interactive_variable};
#[derive(Serialize, Deserialize, Args, Clone)]
pub struct S3ConfigCli {
secret_key: Option<String>,
key_id: Option<String>,
region: Option<String>,
bucket_name: Option<String>,
endpoint: Option<String>,
}
impl From<S3ConfigCli> for S3Config {
fn from(value: S3ConfigCli) -> Self {
interactive_variable!(value, secret_key, "S3 Secret Key");
interactive_variable!(value, key_id, "S3 Key ID");
interactive_variable!(value, region, "S3 Region");
interactive_variable!(value, bucket_name, "S3 Bucket Name");
interactive_optional_variable!(value, endpoint, "S3 Endpoint (leave blank for none");
Self {
secret_key,
key_id,
region,
bucket_name,
endpoint,
}
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct S3Config {
secret_key: String,
key_id: String,
region: String,
bucket_name: String,
endpoint: Option<String>,
}
impl S3Config {
pub fn generate_bucket(&self) -> anyhow::Result<s3::Bucket> {
let credentials =
Credentials::new(Some(&self.key_id), Some(&self.secret_key), None, None, None)?;
let region = if let Some(endpoint) = &self.endpoint {
Region::Custom {
region: self.region.clone(),
endpoint: endpoint.clone(),
}
} else {
Region::from_str(&self.region)?
};
let bucket = Bucket::new(&self.bucket_name, region, credentials)?;
Ok(*bucket)
}
}
impl Configurable for S3Config {
fn configure(&self, config: &mut super::config::Config) {
println!("Configuring S3Config with {:?}", self);
}
}
+18
View File
@@ -0,0 +1,18 @@
use clap::Args;
use serde::{Deserialize, Serialize};
use crate::config::configurable::Configurable;
#[derive(Serialize, Deserialize, Args, Clone)]
pub struct ServerConfig {
/// Endpoint of the Drop server
url: String,
#[arg(short, long)]
token: String,
}
impl Configurable for ServerConfig {
fn configure(&self, config: &mut super::config::Config) {
println!("Configured ServerConfig")
}
}
+47
View File
@@ -0,0 +1,47 @@
use std::str::FromStr;
use dialoguer::{Input, theme::ColorfulTheme};
#[macro_export]
macro_rules! interactive_variable {
($value:ident, $var:ident, $prompt:expr) => {
let $var = if let Some($var) = $value.$var {
$var
} else {
crate::interactive::query_variable($prompt).unwrap()
};
};
}
#[macro_export]
macro_rules! interactive_optional_variable {
($value:ident, $var:ident, $prompt:expr) => {
let $var = if let Some($var) = $value.$var {
Some($var)
} else {
crate::interactive::query_optional_variable($prompt).unwrap()
};
};
}
pub fn query_variable<T: Clone + FromStr + ToString>(prompt: impl ToString) -> dialoguer::Result<T>
where
<T as FromStr>::Err: ToString,
{
Input::with_theme(&ColorfulTheme::default())
.with_prompt(prompt.to_string())
.interact_text()
}
pub fn query_optional_variable<T: Clone + FromStr + ToString>(
prompt: impl ToString,
) -> dialoguer::Result<Option<T>>
where
<T as FromStr>::Err: ToString,
{
let input: T = Input::with_theme(&ColorfulTheme::default())
.with_prompt(prompt.to_string())
.allow_empty(true)
.interact_text()?;
if input.to_string().len() == 0 {
return Ok(None);
}
Ok(Some(input))
}
+22 -7
View File
@@ -1,6 +1,11 @@
use crate::{
cli::{Cli, Commands},
commands::{configure::interactive_configure, upload}, config::Config,
commands::{configure::interactive_configure, upload},
config::{
config::{Config, ConfigOption},
configurable::Configurable,
s3::S3Config,
},
};
use clap::Parser;
use fern::colors::{Color, ColoredLevelConfig};
@@ -13,17 +18,18 @@ mod commands;
mod config;
mod manifest;
#[macro_use]
pub mod interactive;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
configure_logging()?;
let cli = Cli::parse();
let config = Config::new();
let mut config = Config::read();
match &cli.command {
Commands::Configure { url, token } => {
if let Some(token) = token {
} else {
interactive_configure(url.to_string()).await?;
}
Commands::Configure(options) => {
configure_command(&mut config, options).await?;
}
Commands::Upload(info) => {
upload::interface::upload(info, config).await?;
@@ -33,6 +39,15 @@ async fn main() -> anyhow::Result<()> {
Ok(())
}
async fn configure_command(config: &mut Config, options: &ConfigOption) -> anyhow::Result<()> {
let configuration: Box<dyn Configurable> = match options {
ConfigOption::Server(options) => Box::new(options.clone()),
ConfigOption::S3(options) => Box::new(S3Config::from(options.clone())),
};
configuration.configure(config);
Ok(())
}
pub fn configure_logging() -> anyhow::Result<()> {
let log_level = env::var("RUST_LOG")
.unwrap_or_else(|_| "info".to_string())