From 03bea24f9de698375033af92a08762446d0e20cc Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 25 Feb 2024 16:14:56 +0100 Subject: Closes #2. Config and setup stuff --- Cargo.lock | 253 ++++++++++++++++++++++++++++++++++++------------- Cargo.toml | 25 +++-- docker-compose.yml | 8 +- src/config.rs | 30 +++--- src/error.rs | 10 +- src/main.rs | 84 ++++++++-------- src/requests.rs | 2 + src/requests/device.rs | 62 ++++++------ src/requests/mod.rs | 2 - src/requests/start.rs | 103 +++++++++++--------- 10 files changed, 360 insertions(+), 219 deletions(-) create mode 100644 src/requests.rs delete mode 100644 src/requests/mod.rs diff --git a/Cargo.lock b/Cargo.lock index 5620d60..ad227e6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,22 +17,11 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.7.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" -dependencies = [ - "getrandom", - "once_cell", - "version_check", -] - [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "96b09b5178381e0874812a9b157f7fe84982617e48f71f4e3235482775e5b540" dependencies = [ "anstyle", "anstyle-parse", @@ -108,12 +97,6 @@ dependencies = [ "rustc-demangle", ] -[[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.5" @@ -131,6 +114,9 @@ name = "bitflags" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" +dependencies = [ + "serde", +] [[package]] name = "block-buffer" @@ -176,9 +162,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clap" -version = "4.4.8" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2275f18819641850fa26c89acc84d465c1bf91ce57bc2748b28c420473352f64" +checksum = "c918d541ef2913577a0f9566e9ce27cb35b6df072075769e0b26cb5a554520da" dependencies = [ "clap_builder", "clap_derive", @@ -186,9 +172,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.4.8" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07cdf1b148b25c1e1f7a42225e30a0d99a615cd4637eae7365548dd4529b95bc" +checksum = "9f3e7391dad68afb0c2ede1bf619f579a3dc9c2ec67f089baa397123a2f3d1eb" dependencies = [ "anstream", "anstyle", @@ -198,18 +184,18 @@ dependencies = [ [[package]] name = "clap_complete" -version = "4.4.4" +version = "4.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bffe91f06a11b4b9420f62103854e90867812cd5d01557f853c5ee8e791b12ae" +checksum = "885e4d7d5af40bfb99ae6f9433e292feac98d452dcb3ec3d25dfe7552b77da8c" dependencies = [ "clap", ] [[package]] name = "clap_derive" -version = "4.4.7" +version = "4.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47" dependencies = [ "heck", "proc-macro2", @@ -219,9 +205,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" [[package]] name = "colorchoice" @@ -231,11 +217,12 @@ checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" [[package]] name = "config" -version = "0.13.3" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d379af7f68bfc21714c6c7dea883544201741d2ce8274bb12fa54f89507f52a7" +checksum = "7328b20597b53c2454f0b1919720c25c7339051c02b72b7e05409e00b14132be" dependencies = [ "async-trait", + "convert_case", "json5", "lazy_static", "nom", @@ -261,6 +248,35 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "const-random" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aaf16c9c2c612020bcfd042e170f6e32de9b9d75adb5277cdbbd2e2c8c8299a" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.9.3" @@ -286,6 +302,12 @@ dependencies = [ "libc", ] +[[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" @@ -335,9 +357,12 @@ dependencies = [ [[package]] name = "dlv-list" -version = "0.3.0" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] [[package]] name = "encode_unicode" @@ -354,6 +379,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + [[package]] name = "errno" version = "0.3.6" @@ -499,8 +530,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap", + "http 0.2.10", + "indexmap 1.9.3", "slab", "tokio", "tokio-util", @@ -512,9 +543,18 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.14.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" [[package]] name = "heck" @@ -539,6 +579,17 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b32afd38673a8016f7c9ae69e5af41a58f81b1d31689040f2f1959594ce194ea" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + [[package]] name = "http-body" version = "0.4.5" @@ -546,7 +597,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" dependencies = [ "bytes", - "http", + "http 0.2.10", "pin-project-lite", ] @@ -573,7 +624,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", + "http 0.2.10", "http-body", "httparse", "httpdate", @@ -616,7 +667,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", +] + +[[package]] +name = "indexmap" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" +dependencies = [ + "equivalent", + "hashbrown 0.14.3", ] [[package]] @@ -807,9 +868,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.18.0" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "openssl" @@ -863,12 +924,12 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "ordered-multimap" -version = "0.4.3" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" dependencies = [ "dlv-list", - "hashbrown", + "hashbrown 0.13.2", ] [[package]] @@ -1032,13 +1093,13 @@ version = "0.11.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "046cd98826c46c2ac8ddecae268eb5c2e58628688a5fc7a2643704a73faba95b" dependencies = [ - "base64 0.21.5", + "base64", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", + "http 0.2.10", "http-body", "hyper", "hyper-tls", @@ -1066,20 +1127,21 @@ dependencies = [ [[package]] name = "ron" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88073939a61e5b7680558e6be56b419e208420c2adb92be54921fa6b72283f1a" +checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94" dependencies = [ - "base64 0.13.1", - "bitflags 1.3.2", + "base64", + "bitflags 2.4.1", "serde", + "serde_derive", ] [[package]] name = "rust-ini" -version = "0.18.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" dependencies = [ "cfg-if", "ordered-multimap", @@ -1173,6 +1235,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "serde_urlencoded" version = "0.7.1" @@ -1238,9 +1309,9 @@ dependencies = [ [[package]] name = "strsim" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" @@ -1307,6 +1378,15 @@ dependencies = [ "syn", ] +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + [[package]] name = "tinyvec" version = "1.6.0" @@ -1324,9 +1404,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.34.0" +version = "1.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0c014766411e834f7af5b8f4cf46257aab4036ca95e9d2c144a10f59ad6f5b9" +checksum = "61285f6515fa018fb2d1e46eb21223fff441ee8db5d0f1435e8ab4f5cdb80931" dependencies = [ "backtrace", "bytes", @@ -1362,9 +1442,9 @@ dependencies = [ [[package]] name = "tokio-tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "212d5dcb2a1ce06d81107c3d0ffa3121fe974b73f068c8282cb1c32328113b6c" +checksum = "c83b561d025642014097b66e6c1bb422783339e0909e4429cde4749d1990bc38" dependencies = [ "futures-util", "log", @@ -1388,11 +1468,36 @@ dependencies = [ [[package]] name = "toml" -version = "0.5.11" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a9aad4a3066010876e8dcf5a8a06e70a558751117a145c6ce2b82c2e2054290" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f7f0dd8d50a853a531c426359045b1998f04219d88799810762cd4ad314234" +checksum = "2c1b5fd4128cc8d3e0cb74d4ed9a9cc7c7284becd4df68f5f940e1ad123606f6" dependencies = [ + "indexmap 2.2.3", "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -1428,14 +1533,14 @@ checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" [[package]] name = "tungstenite" -version = "0.20.1" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e3dac10fd62eaf6617d3a904ae222845979aec67c615d1c842b4002c7666fb9" +checksum = "9ef1a641ea34f399a848dea702823bbecfb4c486f911735368f1f137cb8257e1" dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 1.0.0", "httparse", "log", "rand", @@ -1478,6 +1583,12 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-segmentation" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" + [[package]] name = "unicode-width" version = "0.1.11" @@ -1612,7 +1723,7 @@ dependencies = [ [[package]] name = "webol-cli" -version = "0.1.0" +version = "0.2.0" dependencies = [ "clap", "clap_complete", @@ -1620,7 +1731,6 @@ dependencies = [ "dirs", "futures-util", "indicatif", - "once_cell", "reqwest", "serde", "serde_json", @@ -1782,6 +1892,15 @@ version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +[[package]] +name = "winnow" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a4191c47f15cc3ec71fcb4913cb83d58def65dd3787610213c649283b5ce178" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.50.0" diff --git a/Cargo.toml b/Cargo.toml index ca76349..461b0bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "webol-cli" -version = "0.1.0" +version = "0.2.0" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,15 +10,14 @@ name = "webol" path = "src/main.rs" [dependencies] -clap = { version = "4.4.6", features = ["derive"] } -clap_complete = "4.4.4" -config = "0.13.3" -dirs = "5.0.1" -futures-util = "0.3.29" -indicatif = "0.17.7" -once_cell = "1.18.0" -reqwest = { version = "0.11.22", features = ["blocking"] } -serde = "1.0.189" -serde_json = "1.0.107" -tokio = { version = "1.33.0", features = ["macros", "rt-multi-thread", "io-std"] } -tokio-tungstenite = "0.20.1" +clap = { version = "4.5", features = ["derive"] } +clap_complete = "4.5" +config = "0.14" +dirs = "5.0" +futures-util = "0.3" +indicatif = "0.17" +reqwest = { version = "0.11", features = ["blocking"] } +serde = "1.0" +serde_json = "1.0" +tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "io-std"] } +tokio-tungstenite = "0.21" diff --git a/docker-compose.yml b/docker-compose.yml index 3a0ade5..f41f4c9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,20 +1,20 @@ services: webol: - image: ghcr.io/fxqnlr/webol:dev-6 - container_name: webol + image: ghcr.io/fxqnlr/webol:dev-9 + container_name: webol-cli-server restart: no depends_on: - db environment: - RUST_LOG=info,webol=trace - WEBOL_DATABASE_URL=postgres://postgres:postgres@localhost:5432/webol - - WEBOL_APIKEY=aaa + - WEBOL_APIKEY=dev - WEBOL_SERVERADDR=127.0.0.1:7229 network_mode: host db: image: postgres - container_name: webol-db + container_name: webol-cli-db restart: no environment: POSTGRES_PASSWORD: postgres diff --git a/src/config.rs b/src/config.rs index 9a9e44b..78795a3 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,19 +1,19 @@ -use config::Config; -use once_cell::sync::Lazy; +use serde::Deserialize; -pub static SETTINGS: Lazy = Lazy::new(setup); - -fn setup() -> Config { - #[cfg(not(debug_assertions))] - let builder = Config::builder().add_source(config::File::with_name( - format!("{}/webol-cli.toml", dirs::config_dir().unwrap().to_string_lossy()).as_str(), - )); +#[derive(Deserialize)] +pub struct Config { + pub apikey: String, + pub server: String, +} - #[cfg(debug_assertions)] - let builder = Config::builder().add_source(config::File::with_name("webol-cli.toml")); +impl Config { + pub fn load() -> Result { + let builder = config::Config::builder() + .add_source(config::File::with_name("~/.config/webol-cli.toml")) + .add_source(config::File::with_name("webol-cli.toml")) + .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) + .build()?; - builder - .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) - .build() - .unwrap() + builder.try_deserialize() + } } diff --git a/src/error.rs b/src/error.rs index f15c60a..531528f 100644 --- a/src/error.rs +++ b/src/error.rs @@ -11,11 +11,11 @@ pub enum CliError { impl Debug for CliError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Reqwest(err) => { err.fmt(f) }, - Self::Config(err) => { err.fmt(f) }, - Self::Serde(err) => { err.fmt(f) }, - Self::Parse(err) => { err.fmt(f) }, - Self::WsResponse => { f.write_str("Error in Response") }, + Self::Reqwest(err) => err.fmt(f), + Self::Config(err) => err.fmt(f), + Self::Serde(err) => err.fmt(f), + Self::Parse(err) => err.fmt(f), + Self::WsResponse => f.write_str("Error in Response"), } } } diff --git a/src/main.rs b/src/main.rs index afe6fac..0393183 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,11 @@ use std::{fmt::Display, time::Duration}; -use clap::{Parser, Command, CommandFactory, Subcommand}; -use clap_complete::{generate, Shell, Generator}; -use config::SETTINGS; +use crate::config::Config; +use clap::{Command, CommandFactory, Parser, Subcommand}; +use clap_complete::{generate, Generator, Shell}; use error::CliError; -use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; -use requests::{start::start, device}; +use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; +use requests::{device, start::start}; use reqwest::header::{HeaderMap, HeaderValue}; use serde::Deserialize; @@ -35,7 +35,7 @@ enum Commands { /// id of the device id: String, #[arg(short, long)] - ping: Option + ping: Option, }, Device { #[command(subcommand)] @@ -52,7 +52,7 @@ enum DeviceCmd { id: String, mac: String, broadcast_addr: String, - ip: String + ip: String, }, Get { id: String, @@ -61,29 +61,39 @@ enum DeviceCmd { id: String, mac: String, broadcast_addr: String, - ip: String + ip: String, }, } #[tokio::main] async fn main() -> Result<(), CliError> { + let config = Config::load().map_err(CliError::Config)?; + let cli = Args::parse(); match cli.commands { Commands::Start { id, ping } => { - start(id, ping.unwrap_or(true)).await?; - }, - Commands::Device { devicecmd } => { - match devicecmd { - DeviceCmd::Add { id, mac, broadcast_addr, ip } => { - device::put(id, mac, broadcast_addr, ip).await?; - }, - DeviceCmd::Get { id } => { - device::get(id).await?; - }, - DeviceCmd::Edit { id, mac, broadcast_addr, ip } => { - device::post(id, mac, broadcast_addr, ip).await?; - }, + start(&config, id, ping.unwrap_or(true)).await?; + } + Commands::Device { devicecmd } => match devicecmd { + DeviceCmd::Add { + id, + mac, + broadcast_addr, + ip, + } => { + device::put(&config, id, mac, broadcast_addr, ip).await?; + } + DeviceCmd::Get { id } => { + device::get(&config, id).await?; + } + DeviceCmd::Edit { + id, + mac, + broadcast_addr, + ip, + } => { + device::post(&config, id, mac, broadcast_addr, ip).await?; } }, Commands::CliGen { id } => { @@ -100,29 +110,26 @@ fn print_completions(gen: G, cmd: &mut Command) { generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); } -fn default_headers() -> Result { +fn default_headers(config: &Config) -> Result { let mut map = HeaderMap::new(); - map.append("Accept-Content", HeaderValue::from_str("application/json").unwrap()); - map.append("Content-Type", HeaderValue::from_str("application/json").unwrap()); + map.append( + "Accept-Content", + HeaderValue::from_str("application/json").unwrap(), + ); + map.append( + "Content-Type", + HeaderValue::from_str("application/json").unwrap(), + ); map.append( "Authorization", - HeaderValue::from_str( - SETTINGS.get_string("key") - .map_err(CliError::Config)? - .as_str() - ).unwrap() + HeaderValue::from_str(&config.apikey).unwrap(), ); Ok(map) } -fn format_url(path: &str, protocol: Protocols) -> Result { - Ok(format!( - "{}://{}/{}", - protocol, - SETTINGS.get_string("server").map_err(CliError::Config)?, - path - )) +fn format_url(config: &Config, path: &str, protocol: Protocols) -> Result { + Ok(format!("{}://{}/{}", protocol, config.server, path)) } fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { @@ -137,7 +144,6 @@ fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { fn finish_pb(pb: ProgressBar, message: String, template: &str) { pb.set_style(ProgressStyle::with_template(template).unwrap()); pb.finish_with_message(message); - } enum Protocols { @@ -149,12 +155,12 @@ impl Display for Protocols { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Http => f.write_str("http"), - Self::Websocket => f.write_str("ws") + Self::Websocket => f.write_str("ws"), } } } #[derive(Debug, Deserialize)] struct ErrorResponse { - error: String + error: String, } diff --git a/src/requests.rs b/src/requests.rs new file mode 100644 index 0000000..6855db1 --- /dev/null +++ b/src/requests.rs @@ -0,0 +1,2 @@ +pub mod start; +pub mod device; diff --git a/src/requests/device.rs b/src/requests/device.rs index cbc838e..5003c4a 100644 --- a/src/requests/device.rs +++ b/src/requests/device.rs @@ -1,20 +1,21 @@ -use crate::{error::CliError, default_headers, format_url, Protocols}; +use crate::{config::Config, default_headers, error::CliError, format_url, Protocols}; -pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { - let url = format_url("device", Protocols::Http)?; +pub async fn put( + config: &Config, + id: String, + mac: String, + broadcast_addr: String, + ip: String, +) -> Result<(), CliError> { + let url = format_url(config, "device", Protocols::Http)?; println!("{}", url); let res = reqwest::Client::new() .put(url) - .headers(default_headers()?) - .body( - format!( - r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, - id, - mac, - broadcast_addr, - ip - ) - ) + .headers(default_headers(config)?) + .body(format!( + r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, + id, mac, broadcast_addr, ip + )) .send() .await .map_err(CliError::Reqwest)? @@ -25,13 +26,11 @@ pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) -> Ok(()) } -pub async fn get(id: String) -> Result<(), CliError> { +pub async fn get(config: &Config, id: String) -> Result<(), CliError> { let res = reqwest::Client::new() - .get(format_url("device", Protocols::Http)?) - .headers(default_headers()?) - .body( - format!(r#"{{"id": "{}"}}"#, id) - ) + .get(format_url(config, "device", Protocols::Http)?) + .headers(default_headers(config)?) + .body(format!(r#"{{"id": "{}"}}"#, id)) .send() .await .map_err(CliError::Reqwest)? @@ -42,19 +41,20 @@ pub async fn get(id: String) -> Result<(), CliError> { Ok(()) } -pub async fn post(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { +pub async fn post( + config: &Config, + id: String, + mac: String, + broadcast_addr: String, + ip: String, +) -> Result<(), CliError> { let res = reqwest::Client::new() - .post(format_url("device", Protocols::Http)?) - .headers(default_headers()?) - .body( - format!( - r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, - id, - mac, - broadcast_addr, - ip - ) - ) + .post(format_url(config, "device", Protocols::Http)?) + .headers(default_headers(config)?) + .body(format!( + r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, + id, mac, broadcast_addr, ip + )) .send() .await .map_err(CliError::Reqwest)? diff --git a/src/requests/mod.rs b/src/requests/mod.rs deleted file mode 100644 index 6855db1..0000000 --- a/src/requests/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod start; -pub mod device; diff --git a/src/requests/start.rs b/src/requests/start.rs index ca4ca44..bc63303 100644 --- a/src/requests/start.rs +++ b/src/requests/start.rs @@ -1,25 +1,26 @@ -use futures_util::{StreamExt, SinkExt}; +use futures_util::{SinkExt, StreamExt}; use indicatif::{MultiProgress, ProgressBar}; use reqwest::StatusCode; use serde::Deserialize; use tokio_tungstenite::{connect_async, tungstenite::Message}; -use crate::{error::CliError, default_headers, ErrorResponse, format_url, Protocols, OVERVIEW_STYLE, DEFAULT_STYLE, DONE_STYLE, finish_pb, ERROR_STYLE, OVERVIEW_ERROR, OVERVIEW_DONE, add_pb}; - -pub async fn start(id: String, ping: bool) -> Result<(), CliError> { +use crate::{ + add_pb, config::Config, default_headers, error::CliError, finish_pb, format_url, ErrorResponse, + Protocols, DEFAULT_STYLE, DONE_STYLE, ERROR_STYLE, OVERVIEW_DONE, OVERVIEW_ERROR, + OVERVIEW_STYLE, +}; +pub async fn start(config: &Config, id: String, ping: bool) -> Result<(), CliError> { let send_start = MultiProgress::new(); let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {}", id)); // TODO: calculate average start-time on server - let url = format_url("start", Protocols::Http)?; + let url = format_url(config, "start", Protocols::Http)?; let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url)); let res = reqwest::Client::new() .post(url) - .headers(default_headers()?) - .body( - format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping) - ) + .headers(default_headers(config)?) + .body(format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping)) .send() .await .map_err(CliError::Reqwest)?; @@ -29,7 +30,7 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { match res.status() { StatusCode::OK => { let body = serde_json::from_str::( - &res.text().await.map_err(CliError::Reqwest)? + &res.text().await.map_err(CliError::Reqwest)?, ) .map_err(CliError::Serde)?; @@ -38,17 +39,25 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { } if ping { - let status = status_socket(body.uuid, &send_start, &overview, id).await?; + let status = status_socket(config, body.uuid, &send_start, &overview, id).await?; if status { - finish_pb(overview, format!("successfully started {}", body.id), OVERVIEW_DONE); + finish_pb( + overview, + format!("successfully started {}", body.id), + OVERVIEW_DONE, + ); } else { - finish_pb(overview, format!("error while starting {}", body.id), OVERVIEW_ERROR); + finish_pb( + overview, + format!("error while starting {}", body.id), + OVERVIEW_ERROR, + ); } } - }, + } _ => { let body = serde_json::from_str::( - &res.text().await.map_err(CliError::Reqwest)? + &res.text().await.map_err(CliError::Reqwest)?, ) .map_err(CliError::Serde)?; @@ -59,16 +68,22 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { Ok(()) } -async fn status_socket(uuid: String, pb: &MultiProgress, overview: &ProgressBar, id: String) -> Result { - // TODO: Remove unwraps +async fn status_socket( + config: &Config, + uuid: String, + pb: &MultiProgress, + overview: &ProgressBar, + id: String, +) -> Result { let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); - let (mut ws_stream, _response) = connect_async(format_url("status", Protocols::Websocket)?) - .await - .expect("Failed to connect"); + let (mut ws_stream, _response) = + connect_async(format_url(config, "status", Protocols::Websocket)?) + .await + .expect("Failed to connect"); finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE); - + ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); - + // Get ETA let eta_msg = ws_stream.next().await.unwrap().unwrap(); let eta = get_eta(eta_msg.into_text().unwrap(), uuid.clone())? + overview.elapsed().as_secs(); @@ -86,29 +101,29 @@ async fn status_socket(uuid: String, pb: &MultiProgress, overview: &ProgressBar, Verified::WrongUuid => { finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); Ok(false) - }, - Verified::ResponseType(res_type) => { - match res_type { - ResponseType::Start => { - finish_pb(v_pb, "device started".to_string(), DONE_STYLE); - Ok(true) - }, - ResponseType::Timeout => { - finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE); - Ok(false) - }, - ResponseType::NotFound => { - finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE); - Ok(false) - }, - } } + Verified::ResponseType(res_type) => match res_type { + ResponseType::Start => { + finish_pb(v_pb, "device started".to_string(), DONE_STYLE); + Ok(true) + } + ResponseType::Timeout => { + finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE); + Ok(false) + } + ResponseType::NotFound => { + finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE); + Ok(false) + } + }, } } fn get_eta(msg: String, uuid: String) -> Result { let spl: Vec<&str> = msg.split('_').collect(); - if (spl[0] != "eta") || (spl[2] != uuid) { return Err(CliError::WsResponse); }; + if (spl[0] != "eta") || (spl[2] != uuid) { + return Err(CliError::WsResponse); + }; Ok(u64::from_str_radix(spl[1], 10).map_err(CliError::Parse)?) } @@ -116,9 +131,11 @@ fn verify_response(res: String, org_uuid: String) -> Result let spl: Vec<&str> = res.split('_').collect(); let res_type = spl[0]; let uuid = spl[1]; - - if uuid != org_uuid { return Ok(Verified::WrongUuid) }; - + + if uuid != org_uuid { + return Ok(Verified::WrongUuid); + }; + Ok(Verified::ResponseType(ResponseType::from(res_type)?)) } @@ -131,7 +148,7 @@ struct StartResponse { enum Verified { ResponseType(ResponseType), - WrongUuid + WrongUuid, } enum ResponseType { -- cgit v1.2.3 From 465a71b6780921fb7ec19682702cbe864decd212 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 25 Feb 2024 16:54:03 +0100 Subject: Closes #3. Use thiserror. Fix clippy stuff --- Cargo.lock | 21 +++++----- Cargo.toml | 1 + src/config.rs | 4 +- src/error.rs | 47 +++++++++++++-------- src/main.rs | 22 +++++----- src/requests/device.rs | 39 ++++++++---------- src/requests/start.rs | 108 ++++++++++++++++++++++--------------------------- 7 files changed, 121 insertions(+), 121 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ad227e6..c0f07f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1021,18 +1021,18 @@ checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" [[package]] name = "proc-macro2" -version = "1.0.69" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -1315,9 +1315,9 @@ checksum = "5ee073c9e4cd00e28217186dbe12796d692868f432bf2e97ee73bed0c56dfa01" [[package]] name = "syn" -version = "2.0.39" +version = "2.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "74f1bdc9872430ce9b75da68329d1c1746faf50ffac5f19e02b71e37ff881ffb" dependencies = [ "proc-macro2", "quote", @@ -1360,18 +1360,18 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.50" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "1e45bcbe8ed29775f228095caf2cd67af7a4ccf756ebff23a306bf3e8b47b24b" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.50" +version = "1.0.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "a953cb265bef375dae3de6663da4d3804eee9682ea80d8e2542529b73c531c81" dependencies = [ "proc-macro2", "quote", @@ -1734,6 +1734,7 @@ dependencies = [ "reqwest", "serde", "serde_json", + "thiserror", "tokio", "tokio-tungstenite", ] diff --git a/Cargo.toml b/Cargo.toml index 461b0bf..a60d788 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,5 +19,6 @@ indicatif = "0.17" reqwest = { version = "0.11", features = ["blocking"] } serde = "1.0" serde_json = "1.0" +thiserror = "1.0.57" tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "io-std"] } tokio-tungstenite = "0.21" diff --git a/src/config.rs b/src/config.rs index 78795a3..d28e111 100644 --- a/src/config.rs +++ b/src/config.rs @@ -9,8 +9,8 @@ pub struct Config { impl Config { pub fn load() -> Result { let builder = config::Config::builder() - .add_source(config::File::with_name("~/.config/webol-cli.toml")) - .add_source(config::File::with_name("webol-cli.toml")) + .add_source(config::File::with_name("~/.config/webol-cli").required(false)) + .add_source(config::File::with_name("webol-cli").required(false)) .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) .build()?; diff --git a/src/error.rs b/src/error.rs index 531528f..15e4308 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,21 +1,34 @@ use std::{fmt::Debug, num::ParseIntError}; -pub enum CliError { - Reqwest(reqwest::Error), - Config(config::ConfigError), - Serde(serde_json::Error), - Parse(ParseIntError), - WsResponse, -} +use reqwest::header::InvalidHeaderValue; -impl Debug for CliError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Self::Reqwest(err) => err.fmt(f), - Self::Config(err) => err.fmt(f), - Self::Serde(err) => err.fmt(f), - Self::Parse(err) => err.fmt(f), - Self::WsResponse => f.write_str("Error in Response"), - } - } +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("request: {source}")] + Reqwest { + #[from] + source: reqwest::Error, + }, + #[error("config: {source}")] + Config { + #[from] + source: config::ConfigError, + }, + #[error("serde: {source}")] + Serde { + #[from] + source: serde_json::Error, + }, + #[error("parse int: {source}")] + Parse { + #[from] + source: ParseIntError, + }, + #[error("parse header: {source}")] + InvalidHeaderValue { + #[from] + source: InvalidHeaderValue + }, + #[error("ws")] + WsResponse, } diff --git a/src/main.rs b/src/main.rs index 0393183..cdca6cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,7 +3,7 @@ use std::{fmt::Display, time::Duration}; use crate::config::Config; use clap::{Command, CommandFactory, Parser, Subcommand}; use clap_complete::{generate, Generator, Shell}; -use error::CliError; +use error::Error; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use requests::{device, start::start}; use reqwest::header::{HeaderMap, HeaderValue}; @@ -66,8 +66,8 @@ enum DeviceCmd { } #[tokio::main] -async fn main() -> Result<(), CliError> { - let config = Config::load().map_err(CliError::Config)?; +async fn main() -> Result<(), Error> { + let config = Config::load()?; let cli = Args::parse(); @@ -99,7 +99,7 @@ async fn main() -> Result<(), CliError> { Commands::CliGen { id } => { eprintln!("Generating completion file for {id:?}..."); let mut cmd = Args::command(); - print_completions(id, &mut cmd) + print_completions(id, &mut cmd); } } @@ -110,26 +110,26 @@ fn print_completions(gen: G, cmd: &mut Command) { generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); } -fn default_headers(config: &Config) -> Result { +fn default_headers(config: &Config) -> Result { let mut map = HeaderMap::new(); map.append( "Accept-Content", - HeaderValue::from_str("application/json").unwrap(), + HeaderValue::from_str("application/json")? ); map.append( "Content-Type", - HeaderValue::from_str("application/json").unwrap(), + HeaderValue::from_str("application/json")? ); map.append( "Authorization", - HeaderValue::from_str(&config.apikey).unwrap(), + HeaderValue::from_str(&config.apikey)? ); Ok(map) } -fn format_url(config: &Config, path: &str, protocol: Protocols) -> Result { - Ok(format!("{}://{}/{}", protocol, config.server, path)) +fn format_url(config: &Config, path: &str, protocol: &Protocols) -> String { + format!("{}://{}/{}", protocol, config.server, path) } fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { @@ -141,7 +141,7 @@ fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { pb } -fn finish_pb(pb: ProgressBar, message: String, template: &str) { +fn finish_pb(pb: &ProgressBar, message: String, template: &str) { pb.set_style(ProgressStyle::with_template(template).unwrap()); pb.finish_with_message(message); } diff --git a/src/requests/device.rs b/src/requests/device.rs index 5003c4a..a612978 100644 --- a/src/requests/device.rs +++ b/src/requests/device.rs @@ -1,4 +1,4 @@ -use crate::{config::Config, default_headers, error::CliError, format_url, Protocols}; +use crate::{config::Config, default_headers, error::Error, format_url, Protocols}; pub async fn put( config: &Config, @@ -6,38 +6,35 @@ pub async fn put( mac: String, broadcast_addr: String, ip: String, -) -> Result<(), CliError> { - let url = format_url(config, "device", Protocols::Http)?; - println!("{}", url); +) -> Result<(), Error> { + let url = format_url(config, "device", &Protocols::Http); + println!("{url}"); let res = reqwest::Client::new() .put(url) .headers(default_headers(config)?) .body(format!( - r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, - id, mac, broadcast_addr, ip + r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#, )) .send() - .await - .map_err(CliError::Reqwest)? + .await? .text() .await; - println!("{:?}", res); + println!("{res:?}"); Ok(()) } -pub async fn get(config: &Config, id: String) -> Result<(), CliError> { +pub async fn get(config: &Config, id: String) -> Result<(), Error> { let res = reqwest::Client::new() - .get(format_url(config, "device", Protocols::Http)?) + .get(format_url(config, "device", &Protocols::Http)) .headers(default_headers(config)?) - .body(format!(r#"{{"id": "{}"}}"#, id)) + .body(format!(r#"{{"id": "{id}"}}"#)) .send() - .await - .map_err(CliError::Reqwest)? + .await? .text() .await; - println!("{:?}", res); + println!("{res:?}"); Ok(()) } @@ -47,20 +44,18 @@ pub async fn post( mac: String, broadcast_addr: String, ip: String, -) -> Result<(), CliError> { +) -> Result<(), Error> { let res = reqwest::Client::new() - .post(format_url(config, "device", Protocols::Http)?) + .post(format_url(config, "device", &Protocols::Http)) .headers(default_headers(config)?) .body(format!( - r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, - id, mac, broadcast_addr, ip + r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#, )) .send() - .await - .map_err(CliError::Reqwest)? + .await? .text() .await; - println!("{:?}", res); + println!("{res:?}"); Ok(()) } diff --git a/src/requests/start.rs b/src/requests/start.rs index bc63303..7abbbe0 100644 --- a/src/requests/start.rs +++ b/src/requests/start.rs @@ -5,64 +5,54 @@ use serde::Deserialize; use tokio_tungstenite::{connect_async, tungstenite::Message}; use crate::{ - add_pb, config::Config, default_headers, error::CliError, finish_pb, format_url, ErrorResponse, + add_pb, config::Config, default_headers, error::Error, finish_pb, format_url, ErrorResponse, Protocols, DEFAULT_STYLE, DONE_STYLE, ERROR_STYLE, OVERVIEW_DONE, OVERVIEW_ERROR, OVERVIEW_STYLE, }; -pub async fn start(config: &Config, id: String, ping: bool) -> Result<(), CliError> { +pub async fn start(config: &Config, id: String, ping: bool) -> Result<(), Error> { let send_start = MultiProgress::new(); - let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {}", id)); + let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {id}")); - // TODO: calculate average start-time on server - let url = format_url(config, "start", Protocols::Http)?; - let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url)); + let url = format_url(config, "start", &Protocols::Http); + let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {url}")); let res = reqwest::Client::new() .post(url) .headers(default_headers(config)?) - .body(format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping)) + .body(format!(r#"{{"id": "{id}", "ping": {ping}}}"#)) .send() - .await - .map_err(CliError::Reqwest)?; - finish_pb(connect, "connected, got response".to_string(), DONE_STYLE); + .await?; + finish_pb(&connect, "connected, got response".to_string(), DONE_STYLE); let res_pb = add_pb(&send_start, DEFAULT_STYLE, "analyzing response".to_string()); - match res.status() { - StatusCode::OK => { - let body = serde_json::from_str::( - &res.text().await.map_err(CliError::Reqwest)?, - ) - .map_err(CliError::Serde)?; - - if body.boot { - finish_pb(res_pb, "sent start packet".to_string(), DONE_STYLE); - } - if ping { - let status = status_socket(config, body.uuid, &send_start, &overview, id).await?; - if status { - finish_pb( - overview, - format!("successfully started {}", body.id), - OVERVIEW_DONE, - ); - } else { - finish_pb( - overview, - format!("error while starting {}", body.id), - OVERVIEW_ERROR, - ); - } - } + if res.status() == StatusCode::OK { + let body = serde_json::from_str::(&res.text().await?)?; + + if body.boot { + finish_pb(&res_pb, "sent start packet".to_string(), DONE_STYLE); } - _ => { - let body = serde_json::from_str::( - &res.text().await.map_err(CliError::Reqwest)?, - ) - .map_err(CliError::Serde)?; - res_pb.finish_with_message(format!("✗ got error: {}", body.error)); + if ping { + let status = status_socket(config, body.uuid, &send_start, &overview, id).await?; + if status { + finish_pb( + &overview, + format!("successfully started {}", body.id), + OVERVIEW_DONE, + ); + } else { + finish_pb( + &overview, + format!("error while starting {}", body.id), + OVERVIEW_ERROR, + ); + } } + } else { + let body = serde_json::from_str::(&res.text().await?)?; + + res_pb.finish_with_message(format!("✗ got error: {}", body.error)); } Ok(()) @@ -74,60 +64,60 @@ async fn status_socket( pb: &MultiProgress, overview: &ProgressBar, id: String, -) -> Result { +) -> Result { let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); let (mut ws_stream, _response) = - connect_async(format_url(config, "status", Protocols::Websocket)?) + connect_async(format_url(config, "status", &Protocols::Websocket)) .await .expect("Failed to connect"); - finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE); + finish_pb(&ws_pb, "connected to websocket".to_string(), DONE_STYLE); ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); // Get ETA let eta_msg = ws_stream.next().await.unwrap().unwrap(); - let eta = get_eta(eta_msg.into_text().unwrap(), uuid.clone())? + overview.elapsed().as_secs(); - overview.set_message(format!("/{}) start {}", eta, id)); + let eta = get_eta(&eta_msg.into_text().unwrap(), &uuid)? + overview.elapsed().as_secs(); + overview.set_message(format!("/{eta}) start {id}")); let msg_pb = add_pb(pb, DEFAULT_STYLE, "await message".to_string()); let msg = ws_stream.next().await.unwrap(); - finish_pb(msg_pb, "received message".to_string(), DONE_STYLE); + finish_pb(&msg_pb, "received message".to_string(), DONE_STYLE); ws_stream.close(None).await.unwrap(); let v_pb = add_pb(pb, DEFAULT_STYLE, "verify response".to_string()); - let res = verify_response(msg.unwrap().to_string(), uuid)?; + let res = verify_response(&msg.unwrap().to_string(), &uuid)?; match res { Verified::WrongUuid => { - finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); + finish_pb(&v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); Ok(false) } Verified::ResponseType(res_type) => match res_type { ResponseType::Start => { - finish_pb(v_pb, "device started".to_string(), DONE_STYLE); + finish_pb(&v_pb, "device started".to_string(), DONE_STYLE); Ok(true) } ResponseType::Timeout => { - finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE); + finish_pb(&v_pb, "ping timed out".to_string(), ERROR_STYLE); Ok(false) } ResponseType::NotFound => { - finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE); + finish_pb(&v_pb, "unknown uuid".to_string(), ERROR_STYLE); Ok(false) } }, } } -fn get_eta(msg: String, uuid: String) -> Result { +fn get_eta(msg: &str, uuid: &str) -> Result { let spl: Vec<&str> = msg.split('_').collect(); if (spl[0] != "eta") || (spl[2] != uuid) { - return Err(CliError::WsResponse); + return Err(Error::WsResponse); }; - Ok(u64::from_str_radix(spl[1], 10).map_err(CliError::Parse)?) + Ok(spl[1].parse()?) } -fn verify_response(res: String, org_uuid: String) -> Result { +fn verify_response(res: &str, org_uuid: &str) -> Result { let spl: Vec<&str> = res.split('_').collect(); let res_type = spl[0]; let uuid = spl[1]; @@ -158,12 +148,12 @@ enum ResponseType { } impl ResponseType { - fn from(value: &str) -> Result { + fn from(value: &str) -> Result { match value { "start" => Ok(ResponseType::Start), "timeout" => Ok(ResponseType::Timeout), "notfound" => Ok(ResponseType::NotFound), - _ => Err(CliError::WsResponse), + _ => Err(Error::WsResponse), } } } -- cgit v1.2.3 From a192e9baca9a14beaa9f87c27a63cff96aa41c94 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 25 Feb 2024 20:00:38 +0100 Subject: Closes #4. Auth on Websocket. Small stuff --- Cargo.lock | 7 +++++++ Cargo.toml | 1 + src/error.rs | 13 +++++++++++-- src/main.rs | 33 +++++++++++++++++++-------------- src/requests/device.rs | 23 ++++++++++------------- src/requests/start.rs | 24 ++++++++++++++++++------ 6 files changed, 66 insertions(+), 35 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c0f07f7..25f23fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,6 +65,12 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "anyhow" +version = "1.0.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ad32ce52e4161730f7098c077cd2ed6229b5804ccf99e5366be1ab72a98b4e1" + [[package]] name = "async-trait" version = "0.1.74" @@ -1725,6 +1731,7 @@ dependencies = [ name = "webol-cli" version = "0.2.0" dependencies = [ + "anyhow", "clap", "clap_complete", "config", diff --git a/Cargo.toml b/Cargo.toml index a60d788..4791c6c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ name = "webol" path = "src/main.rs" [dependencies] +anyhow = "1.0" clap = { version = "4.5", features = ["derive"] } clap_complete = "4.5" config = "0.14" diff --git a/src/error.rs b/src/error.rs index 15e4308..1e6eac1 100644 --- a/src/error.rs +++ b/src/error.rs @@ -27,8 +27,17 @@ pub enum Error { #[error("parse header: {source}")] InvalidHeaderValue { #[from] - source: InvalidHeaderValue + source: InvalidHeaderValue, }, - #[error("ws")] + #[error("tungstenite: {source}")] + Tungstenite { + #[from] + source: tokio_tungstenite::tungstenite::Error, + }, + #[error("faulty websocket response")] WsResponse, + #[error("authorization failed")] + Authorization, + #[error("Http error status: {0}")] + HttpStatus(u16), } diff --git a/src/main.rs b/src/main.rs index cdca6cb..d76341f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,7 +6,10 @@ use clap_complete::{generate, Generator, Shell}; use error::Error; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use requests::{device, start::start}; -use reqwest::header::{HeaderMap, HeaderValue}; +use reqwest::{ + header::{HeaderMap, HeaderValue}, + Response, +}; use serde::Deserialize; mod config; @@ -66,7 +69,7 @@ enum DeviceCmd { } #[tokio::main] -async fn main() -> Result<(), Error> { +async fn main() -> Result<(), anyhow::Error> { let config = Config::load()?; let cli = Args::parse(); @@ -112,18 +115,9 @@ fn print_completions(gen: G, cmd: &mut Command) { fn default_headers(config: &Config) -> Result { let mut map = HeaderMap::new(); - map.append( - "Accept-Content", - HeaderValue::from_str("application/json")? - ); - map.append( - "Content-Type", - HeaderValue::from_str("application/json")? - ); - map.append( - "Authorization", - HeaderValue::from_str(&config.apikey)? - ); + map.append("Accept-Content", HeaderValue::from_str("application/json")?); + map.append("Content-Type", HeaderValue::from_str("application/json")?); + map.append("Authorization", HeaderValue::from_str(&config.apikey)?); Ok(map) } @@ -132,6 +126,17 @@ fn format_url(config: &Config, path: &str, protocol: &Protocols) -> String { format!("{}://{}/{}", protocol, config.server, path) } +async fn check_success(res: Response) -> Result { + let status = res.status(); + if status.is_success() { + Ok(res.text().await?) + } else if status.as_u16() == 401 { + Err(Error::Authorization) + } else { + Err(Error::HttpStatus(status.as_u16())) + } +} + fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { let pb = mp.add(ProgressBar::new(1)); pb.set_style(ProgressStyle::with_template(template).unwrap()); diff --git a/src/requests/device.rs b/src/requests/device.rs index a612978..7583406 100644 --- a/src/requests/device.rs +++ b/src/requests/device.rs @@ -1,4 +1,4 @@ -use crate::{config::Config, default_headers, error::Error, format_url, Protocols}; +use crate::{check_success, config::Config, default_headers, error::Error, format_url, Protocols}; pub async fn put( config: &Config, @@ -16,11 +16,10 @@ pub async fn put( r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#, )) .send() - .await? - .text() - .await; + .await?; - println!("{res:?}"); + let body = check_success(res).await?; + println!("{body}"); Ok(()) } @@ -30,11 +29,10 @@ pub async fn get(config: &Config, id: String) -> Result<(), Error> { .headers(default_headers(config)?) .body(format!(r#"{{"id": "{id}"}}"#)) .send() - .await? - .text() - .await; + .await?; - println!("{res:?}"); + let body = check_success(res).await?; + println!("{body}"); Ok(()) } @@ -52,10 +50,9 @@ pub async fn post( r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#, )) .send() - .await? - .text() - .await; + .await?; - println!("{res:?}"); + let body = check_success(res).await?; + println!("{body}"); Ok(()) } diff --git a/src/requests/start.rs b/src/requests/start.rs index 7abbbe0..d07177e 100644 --- a/src/requests/start.rs +++ b/src/requests/start.rs @@ -2,7 +2,10 @@ use futures_util::{SinkExt, StreamExt}; use indicatif::{MultiProgress, ProgressBar}; use reqwest::StatusCode; use serde::Deserialize; -use tokio_tungstenite::{connect_async, tungstenite::Message}; +use tokio_tungstenite::{ + connect_async, + tungstenite::{http::Request, Message}, +}; use crate::{ add_pb, config::Config, default_headers, error::Error, finish_pb, format_url, ErrorResponse, @@ -66,17 +69,26 @@ async fn status_socket( id: String, ) -> Result { let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); - let (mut ws_stream, _response) = - connect_async(format_url(config, "status", &Protocols::Websocket)) - .await - .expect("Failed to connect"); + + let request = Request::builder() + .uri(format_url(config, "status", &Protocols::Websocket)) + .header("Authorization", &config.apikey) + .header("sec-websocket-key", "") + .header("host", &config.server) + .header("upgrade", "websocket") + .header("connection", "upgrade") + .header("sec-websocket-version", 13) + .body(()) + .unwrap(); + + let (mut ws_stream, _response) = connect_async(request).await?; finish_pb(&ws_pb, "connected to websocket".to_string(), DONE_STYLE); ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); // Get ETA let eta_msg = ws_stream.next().await.unwrap().unwrap(); - let eta = get_eta(&eta_msg.into_text().unwrap(), &uuid)? + overview.elapsed().as_secs(); + let eta = get_eta(&eta_msg.into_text().unwrap(), &uuid)?; overview.set_message(format!("/{eta}) start {id}")); let msg_pb = add_pb(pb, DEFAULT_STYLE, "await message".to_string()); -- cgit v1.2.3 From 3a2dc1f2741f7bf4d72c1cbe0fd1993af157ceaa Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Sun, 25 Feb 2024 20:50:50 +0100 Subject: Closes #5. Eta works --- docker-compose.yml | 2 +- src/main.rs | 6 +++--- src/requests/start.rs | 10 ++++++++-- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index f41f4c9..ea10db2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,7 +9,7 @@ services: - RUST_LOG=info,webol=trace - WEBOL_DATABASE_URL=postgres://postgres:postgres@localhost:5432/webol - WEBOL_APIKEY=dev - - WEBOL_SERVERADDR=127.0.0.1:7229 + - WEBOL_SERVERADDR=0.0.0.0:7229 network_mode: host db: diff --git a/src/main.rs b/src/main.rs index d76341f..5a0931d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,9 +16,9 @@ mod config; mod error; mod requests; -static OVERVIEW_STYLE: &str = "{spinner:.green} ({elapsed}{wide_msg}"; -static OVERVIEW_ERROR: &str = "✗ ({elapsed}) {wide_msg}"; -static OVERVIEW_DONE: &str = "✓ ({elapsed}) {wide_msg}"; +static OVERVIEW_STYLE: &str = "{spinner:.green} ({elapsed_precise}{wide_msg}"; +static OVERVIEW_ERROR: &str = "✗ ({elapsed_precise}) {wide_msg}"; +static OVERVIEW_DONE: &str = "✓ ({elapsed_precise}) {wide_msg}"; static DEFAULT_STYLE: &str = " {spinner:.green} {wide_msg}"; static DONE_STYLE: &str = " ✓ {wide_msg}"; static ERROR_STYLE: &str = " ✗ {wide_msg}"; diff --git a/src/requests/start.rs b/src/requests/start.rs index d07177e..3afbe3a 100644 --- a/src/requests/start.rs +++ b/src/requests/start.rs @@ -121,12 +121,18 @@ async fn status_socket( } } -fn get_eta(msg: &str, uuid: &str) -> Result { +fn get_eta(msg: &str, uuid: &str) -> Result { let spl: Vec<&str> = msg.split('_').collect(); if (spl[0] != "eta") || (spl[2] != uuid) { return Err(Error::WsResponse); }; - Ok(spl[1].parse()?) + let input: u64 = spl[1].parse()?; + + let sec = input % 60; + let min = (input / 60) % 60; + let hou = (input / (60 * 60)) % 60; + + Ok(format!("{hou:0>2}:{min:0>2}:{sec:0>2}")) } fn verify_response(res: &str, org_uuid: &str) -> Result { -- cgit v1.2.3 From ffedfd00a46147b225c834187fc298e88e60d0c5 Mon Sep 17 00:00:00 2001 From: FxQnLr Date: Mon, 26 Feb 2024 12:43:35 +0100 Subject: Closes #6. Should autodetect config dir --- Cargo.toml | 2 +- src/config.rs | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 4791c6c..36b08c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,6 @@ indicatif = "0.17" reqwest = { version = "0.11", features = ["blocking"] } serde = "1.0" serde_json = "1.0" -thiserror = "1.0.57" +thiserror = "1.0" tokio = { version = "1.36", features = ["macros", "rt-multi-thread", "io-std"] } tokio-tungstenite = "0.21" diff --git a/src/config.rs b/src/config.rs index d28e111..769269c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -8,12 +8,23 @@ pub struct Config { impl Config { pub fn load() -> Result { - let builder = config::Config::builder() - .add_source(config::File::with_name("~/.config/webol-cli").required(false)) + let config_dir = dirs::config_dir(); + + let builder = config::Config::builder(); + + let builder = if let Some(conf) = config_dir { + let dir = conf.to_string_lossy(); + builder.add_source(config::File::with_name(format!("{dir}/webol-cli").as_str()).required(false)) + } else { + println!("!No config dir found"); + builder + }; + + let build = builder .add_source(config::File::with_name("webol-cli").required(false)) - .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) + .add_source(config::Environment::with_prefix("WEBOL_CLI").separator("_")) .build()?; - builder.try_deserialize() + build.try_deserialize() } } -- cgit v1.2.3