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 --- src/config.rs | 4 +- src/error.rs | 47 +++++++++++++-------- src/main.rs | 22 +++++----- src/requests/device.rs | 39 ++++++++---------- src/requests/start.rs | 108 ++++++++++++++++++++++--------------------------- 5 files changed, 109 insertions(+), 111 deletions(-) (limited to 'src') 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