summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/config.rs39
-rw-r--r--src/error.rs56
-rw-r--r--src/main.rs111
-rw-r--r--src/requests.rs (renamed from src/requests/mod.rs)0
-rw-r--r--src/requests/device.rs86
-rw-r--r--src/requests/start.rs189
6 files changed, 271 insertions, 210 deletions
diff --git a/src/config.rs b/src/config.rs
index 9a9e44b..769269c 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -1,19 +1,30 @@
1use config::Config; 1use serde::Deserialize;
2use once_cell::sync::Lazy;
3 2
4pub static SETTINGS: Lazy<Config> = Lazy::new(setup); 3#[derive(Deserialize)]
4pub struct Config {
5 pub apikey: String,
6 pub server: String,
7}
8
9impl Config {
10 pub fn load() -> Result<Config, config::ConfigError> {
11 let config_dir = dirs::config_dir();
12
13 let builder = config::Config::builder();
5 14
6fn setup() -> Config { 15 let builder = if let Some(conf) = config_dir {
7 #[cfg(not(debug_assertions))] 16 let dir = conf.to_string_lossy();
8 let builder = Config::builder().add_source(config::File::with_name( 17 builder.add_source(config::File::with_name(format!("{dir}/webol-cli").as_str()).required(false))
9 format!("{}/webol-cli.toml", dirs::config_dir().unwrap().to_string_lossy()).as_str(), 18 } else {
10 )); 19 println!("!No config dir found");
20 builder
21 };
11 22
12 #[cfg(debug_assertions)] 23 let build = builder
13 let builder = Config::builder().add_source(config::File::with_name("webol-cli.toml")); 24 .add_source(config::File::with_name("webol-cli").required(false))
25 .add_source(config::Environment::with_prefix("WEBOL_CLI").separator("_"))
26 .build()?;
14 27
15 builder 28 build.try_deserialize()
16 .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) 29 }
17 .build()
18 .unwrap()
19} 30}
diff --git a/src/error.rs b/src/error.rs
index f15c60a..1e6eac1 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,21 +1,43 @@
1use std::{fmt::Debug, num::ParseIntError}; 1use std::{fmt::Debug, num::ParseIntError};
2 2
3pub enum CliError { 3use reqwest::header::InvalidHeaderValue;
4 Reqwest(reqwest::Error),
5 Config(config::ConfigError),
6 Serde(serde_json::Error),
7 Parse(ParseIntError),
8 WsResponse,
9}
10 4
11impl Debug for CliError { 5#[derive(Debug, thiserror::Error)]
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 6pub enum Error {
13 match self { 7 #[error("request: {source}")]
14 Self::Reqwest(err) => { err.fmt(f) }, 8 Reqwest {
15 Self::Config(err) => { err.fmt(f) }, 9 #[from]
16 Self::Serde(err) => { err.fmt(f) }, 10 source: reqwest::Error,
17 Self::Parse(err) => { err.fmt(f) }, 11 },
18 Self::WsResponse => { f.write_str("Error in Response") }, 12 #[error("config: {source}")]
19 } 13 Config {
20 } 14 #[from]
15 source: config::ConfigError,
16 },
17 #[error("serde: {source}")]
18 Serde {
19 #[from]
20 source: serde_json::Error,
21 },
22 #[error("parse int: {source}")]
23 Parse {
24 #[from]
25 source: ParseIntError,
26 },
27 #[error("parse header: {source}")]
28 InvalidHeaderValue {
29 #[from]
30 source: InvalidHeaderValue,
31 },
32 #[error("tungstenite: {source}")]
33 Tungstenite {
34 #[from]
35 source: tokio_tungstenite::tungstenite::Error,
36 },
37 #[error("faulty websocket response")]
38 WsResponse,
39 #[error("authorization failed")]
40 Authorization,
41 #[error("Http error status: {0}")]
42 HttpStatus(u16),
21} 43}
diff --git a/src/main.rs b/src/main.rs
index afe6fac..5a0931d 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,21 +1,24 @@
1use std::{fmt::Display, time::Duration}; 1use std::{fmt::Display, time::Duration};
2 2
3use clap::{Parser, Command, CommandFactory, Subcommand}; 3use crate::config::Config;
4use clap_complete::{generate, Shell, Generator}; 4use clap::{Command, CommandFactory, Parser, Subcommand};
5use config::SETTINGS; 5use clap_complete::{generate, Generator, Shell};
6use error::CliError; 6use error::Error;
7use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; 7use indicatif::{MultiProgress, ProgressBar, ProgressStyle};
8use requests::{start::start, device}; 8use requests::{device, start::start};
9use reqwest::header::{HeaderMap, HeaderValue}; 9use reqwest::{
10 header::{HeaderMap, HeaderValue},
11 Response,
12};
10use serde::Deserialize; 13use serde::Deserialize;
11 14
12mod config; 15mod config;
13mod error; 16mod error;
14mod requests; 17mod requests;
15 18
16static OVERVIEW_STYLE: &str = "{spinner:.green} ({elapsed}{wide_msg}"; 19static OVERVIEW_STYLE: &str = "{spinner:.green} ({elapsed_precise}{wide_msg}";
17static OVERVIEW_ERROR: &str = "✗ ({elapsed}) {wide_msg}"; 20static OVERVIEW_ERROR: &str = "✗ ({elapsed_precise}) {wide_msg}";
18static OVERVIEW_DONE: &str = "✓ ({elapsed}) {wide_msg}"; 21static OVERVIEW_DONE: &str = "✓ ({elapsed_precise}) {wide_msg}";
19static DEFAULT_STYLE: &str = " {spinner:.green} {wide_msg}"; 22static DEFAULT_STYLE: &str = " {spinner:.green} {wide_msg}";
20static DONE_STYLE: &str = " ✓ {wide_msg}"; 23static DONE_STYLE: &str = " ✓ {wide_msg}";
21static ERROR_STYLE: &str = " ✗ {wide_msg}"; 24static ERROR_STYLE: &str = " ✗ {wide_msg}";
@@ -35,7 +38,7 @@ enum Commands {
35 /// id of the device 38 /// id of the device
36 id: String, 39 id: String,
37 #[arg(short, long)] 40 #[arg(short, long)]
38 ping: Option<bool> 41 ping: Option<bool>,
39 }, 42 },
40 Device { 43 Device {
41 #[command(subcommand)] 44 #[command(subcommand)]
@@ -52,7 +55,7 @@ enum DeviceCmd {
52 id: String, 55 id: String,
53 mac: String, 56 mac: String,
54 broadcast_addr: String, 57 broadcast_addr: String,
55 ip: String 58 ip: String,
56 }, 59 },
57 Get { 60 Get {
58 id: String, 61 id: String,
@@ -61,35 +64,45 @@ enum DeviceCmd {
61 id: String, 64 id: String,
62 mac: String, 65 mac: String,
63 broadcast_addr: String, 66 broadcast_addr: String,
64 ip: String 67 ip: String,
65 }, 68 },
66} 69}
67 70
68#[tokio::main] 71#[tokio::main]
69async fn main() -> Result<(), CliError> { 72async fn main() -> Result<(), anyhow::Error> {
73 let config = Config::load()?;
74
70 let cli = Args::parse(); 75 let cli = Args::parse();
71 76
72 match cli.commands { 77 match cli.commands {
73 Commands::Start { id, ping } => { 78 Commands::Start { id, ping } => {
74 start(id, ping.unwrap_or(true)).await?; 79 start(&config, id, ping.unwrap_or(true)).await?;
75 }, 80 }
76 Commands::Device { devicecmd } => { 81 Commands::Device { devicecmd } => match devicecmd {
77 match devicecmd { 82 DeviceCmd::Add {
78 DeviceCmd::Add { id, mac, broadcast_addr, ip } => { 83 id,
79 device::put(id, mac, broadcast_addr, ip).await?; 84 mac,
80 }, 85 broadcast_addr,
81 DeviceCmd::Get { id } => { 86 ip,
82 device::get(id).await?; 87 } => {
83 }, 88 device::put(&config, id, mac, broadcast_addr, ip).await?;
84 DeviceCmd::Edit { id, mac, broadcast_addr, ip } => { 89 }
85 device::post(id, mac, broadcast_addr, ip).await?; 90 DeviceCmd::Get { id } => {
86 }, 91 device::get(&config, id).await?;
92 }
93 DeviceCmd::Edit {
94 id,
95 mac,
96 broadcast_addr,
97 ip,
98 } => {
99 device::post(&config, id, mac, broadcast_addr, ip).await?;
87 } 100 }
88 }, 101 },
89 Commands::CliGen { id } => { 102 Commands::CliGen { id } => {
90 eprintln!("Generating completion file for {id:?}..."); 103 eprintln!("Generating completion file for {id:?}...");
91 let mut cmd = Args::command(); 104 let mut cmd = Args::command();
92 print_completions(id, &mut cmd) 105 print_completions(id, &mut cmd);
93 } 106 }
94 } 107 }
95 108
@@ -100,29 +113,28 @@ fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
100 generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); 113 generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
101} 114}
102 115
103fn default_headers() -> Result<HeaderMap, CliError> { 116fn default_headers(config: &Config) -> Result<HeaderMap, Error> {
104 let mut map = HeaderMap::new(); 117 let mut map = HeaderMap::new();
105 map.append("Accept-Content", HeaderValue::from_str("application/json").unwrap()); 118 map.append("Accept-Content", HeaderValue::from_str("application/json")?);
106 map.append("Content-Type", HeaderValue::from_str("application/json").unwrap()); 119 map.append("Content-Type", HeaderValue::from_str("application/json")?);
107 map.append( 120 map.append("Authorization", HeaderValue::from_str(&config.apikey)?);
108 "Authorization",
109 HeaderValue::from_str(
110 SETTINGS.get_string("key")
111 .map_err(CliError::Config)?
112 .as_str()
113 ).unwrap()
114 );
115 121
116 Ok(map) 122 Ok(map)
117} 123}
118 124
119fn format_url(path: &str, protocol: Protocols) -> Result<String, CliError> { 125fn format_url(config: &Config, path: &str, protocol: &Protocols) -> String {
120 Ok(format!( 126 format!("{}://{}/{}", protocol, config.server, path)
121 "{}://{}/{}", 127}
122 protocol, 128
123 SETTINGS.get_string("server").map_err(CliError::Config)?, 129async fn check_success(res: Response) -> Result<String, Error> {
124 path 130 let status = res.status();
125 )) 131 if status.is_success() {
132 Ok(res.text().await?)
133 } else if status.as_u16() == 401 {
134 Err(Error::Authorization)
135 } else {
136 Err(Error::HttpStatus(status.as_u16()))
137 }
126} 138}
127 139
128fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { 140fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar {
@@ -134,10 +146,9 @@ fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar {
134 pb 146 pb
135} 147}
136 148
137fn finish_pb(pb: ProgressBar, message: String, template: &str) { 149fn finish_pb(pb: &ProgressBar, message: String, template: &str) {
138 pb.set_style(ProgressStyle::with_template(template).unwrap()); 150 pb.set_style(ProgressStyle::with_template(template).unwrap());
139 pb.finish_with_message(message); 151 pb.finish_with_message(message);
140
141} 152}
142 153
143enum Protocols { 154enum Protocols {
@@ -149,12 +160,12 @@ impl Display for Protocols {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 160 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 match self { 161 match self {
151 Self::Http => f.write_str("http"), 162 Self::Http => f.write_str("http"),
152 Self::Websocket => f.write_str("ws") 163 Self::Websocket => f.write_str("ws"),
153 } 164 }
154 } 165 }
155} 166}
156 167
157#[derive(Debug, Deserialize)] 168#[derive(Debug, Deserialize)]
158struct ErrorResponse { 169struct ErrorResponse {
159 error: String 170 error: String,
160} 171}
diff --git a/src/requests/mod.rs b/src/requests.rs
index 6855db1..6855db1 100644
--- a/src/requests/mod.rs
+++ b/src/requests.rs
diff --git a/src/requests/device.rs b/src/requests/device.rs
index cbc838e..7583406 100644
--- a/src/requests/device.rs
+++ b/src/requests/device.rs
@@ -1,66 +1,58 @@
1use crate::{error::CliError, default_headers, format_url, Protocols}; 1use crate::{check_success, config::Config, default_headers, error::Error, format_url, Protocols};
2 2
3pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { 3pub async fn put(
4 let url = format_url("device", Protocols::Http)?; 4 config: &Config,
5 println!("{}", url); 5 id: String,
6 mac: String,
7 broadcast_addr: String,
8 ip: String,
9) -> Result<(), Error> {
10 let url = format_url(config, "device", &Protocols::Http);
11 println!("{url}");
6 let res = reqwest::Client::new() 12 let res = reqwest::Client::new()
7 .put(url) 13 .put(url)
8 .headers(default_headers()?) 14 .headers(default_headers(config)?)
9 .body( 15 .body(format!(
10 format!( 16 r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#,
11 r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, 17 ))
12 id,
13 mac,
14 broadcast_addr,
15 ip
16 )
17 )
18 .send() 18 .send()
19 .await 19 .await?;
20 .map_err(CliError::Reqwest)?
21 .text()
22 .await;
23 20
24 println!("{:?}", res); 21 let body = check_success(res).await?;
22 println!("{body}");
25 Ok(()) 23 Ok(())
26} 24}
27 25
28pub async fn get(id: String) -> Result<(), CliError> { 26pub async fn get(config: &Config, id: String) -> Result<(), Error> {
29 let res = reqwest::Client::new() 27 let res = reqwest::Client::new()
30 .get(format_url("device", Protocols::Http)?) 28 .get(format_url(config, "device", &Protocols::Http))
31 .headers(default_headers()?) 29 .headers(default_headers(config)?)
32 .body( 30 .body(format!(r#"{{"id": "{id}"}}"#))
33 format!(r#"{{"id": "{}"}}"#, id)
34 )
35 .send() 31 .send()
36 .await 32 .await?;
37 .map_err(CliError::Reqwest)?
38 .text()
39 .await;
40 33
41 println!("{:?}", res); 34 let body = check_success(res).await?;
35 println!("{body}");
42 Ok(()) 36 Ok(())
43} 37}
44 38
45pub async fn post(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { 39pub async fn post(
40 config: &Config,
41 id: String,
42 mac: String,
43 broadcast_addr: String,
44 ip: String,
45) -> Result<(), Error> {
46 let res = reqwest::Client::new() 46 let res = reqwest::Client::new()
47 .post(format_url("device", Protocols::Http)?) 47 .post(format_url(config, "device", &Protocols::Http))
48 .headers(default_headers()?) 48 .headers(default_headers(config)?)
49 .body( 49 .body(format!(
50 format!( 50 r#"{{"id": "{id}", "mac": "{mac}", "broadcast_addr": "{broadcast_addr}", "ip": "{ip}"}}"#,
51 r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, 51 ))
52 id,
53 mac,
54 broadcast_addr,
55 ip
56 )
57 )
58 .send() 52 .send()
59 .await 53 .await?;
60 .map_err(CliError::Reqwest)?
61 .text()
62 .await;
63 54
64 println!("{:?}", res); 55 let body = check_success(res).await?;
56 println!("{body}");
65 Ok(()) 57 Ok(())
66} 58}
diff --git a/src/requests/start.rs b/src/requests/start.rs
index ca4ca44..3afbe3a 100644
--- a/src/requests/start.rs
+++ b/src/requests/start.rs
@@ -1,124 +1,149 @@
1use futures_util::{StreamExt, SinkExt}; 1use futures_util::{SinkExt, StreamExt};
2use indicatif::{MultiProgress, ProgressBar}; 2use indicatif::{MultiProgress, ProgressBar};
3use reqwest::StatusCode; 3use reqwest::StatusCode;
4use serde::Deserialize; 4use serde::Deserialize;
5use tokio_tungstenite::{connect_async, tungstenite::Message}; 5use tokio_tungstenite::{
6 6 connect_async,
7use 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}; 7 tungstenite::{http::Request, Message},
8 8};
9pub async fn start(id: String, ping: bool) -> Result<(), CliError> { 9
10 10use crate::{
11 add_pb, config::Config, default_headers, error::Error, finish_pb, format_url, ErrorResponse,
12 Protocols, DEFAULT_STYLE, DONE_STYLE, ERROR_STYLE, OVERVIEW_DONE, OVERVIEW_ERROR,
13 OVERVIEW_STYLE,
14};
15
16pub async fn start(config: &Config, id: String, ping: bool) -> Result<(), Error> {
11 let send_start = MultiProgress::new(); 17 let send_start = MultiProgress::new();
12 let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {}", id)); 18 let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {id}"));
13 19
14 // TODO: calculate average start-time on server 20 let url = format_url(config, "start", &Protocols::Http);
15 let url = format_url("start", Protocols::Http)?; 21 let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {url}"));
16 let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url));
17 let res = reqwest::Client::new() 22 let res = reqwest::Client::new()
18 .post(url) 23 .post(url)
19 .headers(default_headers()?) 24 .headers(default_headers(config)?)
20 .body( 25 .body(format!(r#"{{"id": "{id}", "ping": {ping}}}"#))
21 format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping)
22 )
23 .send() 26 .send()
24 .await 27 .await?;
25 .map_err(CliError::Reqwest)?; 28 finish_pb(&connect, "connected, got response".to_string(), DONE_STYLE);
26 finish_pb(connect, "connected, got response".to_string(), DONE_STYLE);
27 29
28 let res_pb = add_pb(&send_start, DEFAULT_STYLE, "analyzing response".to_string()); 30 let res_pb = add_pb(&send_start, DEFAULT_STYLE, "analyzing response".to_string());
29 match res.status() {
30 StatusCode::OK => {
31 let body = serde_json::from_str::<StartResponse>(
32 &res.text().await.map_err(CliError::Reqwest)?
33 )
34 .map_err(CliError::Serde)?;
35
36 if body.boot {
37 finish_pb(res_pb, "sent start packet".to_string(), DONE_STYLE);
38 }
39 31
40 if ping { 32 if res.status() == StatusCode::OK {
41 let status = status_socket(body.uuid, &send_start, &overview, id).await?; 33 let body = serde_json::from_str::<StartResponse>(&res.text().await?)?;
42 if status {
43 finish_pb(overview, format!("successfully started {}", body.id), OVERVIEW_DONE);
44 } else {
45 finish_pb(overview, format!("error while starting {}", body.id), OVERVIEW_ERROR);
46 }
47 }
48 },
49 _ => {
50 let body = serde_json::from_str::<ErrorResponse>(
51 &res.text().await.map_err(CliError::Reqwest)?
52 )
53 .map_err(CliError::Serde)?;
54 34
55 res_pb.finish_with_message(format!("✗ got error: {}", body.error)); 35 if body.boot {
36 finish_pb(&res_pb, "sent start packet".to_string(), DONE_STYLE);
56 } 37 }
38
39 if ping {
40 let status = status_socket(config, body.uuid, &send_start, &overview, id).await?;
41 if status {
42 finish_pb(
43 &overview,
44 format!("successfully started {}", body.id),
45 OVERVIEW_DONE,
46 );
47 } else {
48 finish_pb(
49 &overview,
50 format!("error while starting {}", body.id),
51 OVERVIEW_ERROR,
52 );
53 }
54 }
55 } else {
56 let body = serde_json::from_str::<ErrorResponse>(&res.text().await?)?;
57
58 res_pb.finish_with_message(format!("✗ got error: {}", body.error));
57 } 59 }
58 60
59 Ok(()) 61 Ok(())
60} 62}
61 63
62async fn status_socket(uuid: String, pb: &MultiProgress, overview: &ProgressBar, id: String) -> Result<bool, CliError> { 64async fn status_socket(
63 // TODO: Remove unwraps 65 config: &Config,
66 uuid: String,
67 pb: &MultiProgress,
68 overview: &ProgressBar,
69 id: String,
70) -> Result<bool, Error> {
64 let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); 71 let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string());
65 let (mut ws_stream, _response) = connect_async(format_url("status", Protocols::Websocket)?) 72
66 .await 73 let request = Request::builder()
67 .expect("Failed to connect"); 74 .uri(format_url(config, "status", &Protocols::Websocket))
68 finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE); 75 .header("Authorization", &config.apikey)
69 76 .header("sec-websocket-key", "")
77 .header("host", &config.server)
78 .header("upgrade", "websocket")
79 .header("connection", "upgrade")
80 .header("sec-websocket-version", 13)
81 .body(())
82 .unwrap();
83
84 let (mut ws_stream, _response) = connect_async(request).await?;
85 finish_pb(&ws_pb, "connected to websocket".to_string(), DONE_STYLE);
86
70 ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); 87 ws_stream.send(Message::Text(uuid.clone())).await.unwrap();
71 88
72 // Get ETA 89 // Get ETA
73 let eta_msg = ws_stream.next().await.unwrap().unwrap(); 90 let eta_msg = ws_stream.next().await.unwrap().unwrap();
74 let eta = get_eta(eta_msg.into_text().unwrap(), uuid.clone())? + overview.elapsed().as_secs(); 91 let eta = get_eta(&eta_msg.into_text().unwrap(), &uuid)?;
75 overview.set_message(format!("/{}) start {}", eta, id)); 92 overview.set_message(format!("/{eta}) start {id}"));
76 93
77 let msg_pb = add_pb(pb, DEFAULT_STYLE, "await message".to_string()); 94 let msg_pb = add_pb(pb, DEFAULT_STYLE, "await message".to_string());
78 let msg = ws_stream.next().await.unwrap(); 95 let msg = ws_stream.next().await.unwrap();
79 finish_pb(msg_pb, "received message".to_string(), DONE_STYLE); 96 finish_pb(&msg_pb, "received message".to_string(), DONE_STYLE);
80 97
81 ws_stream.close(None).await.unwrap(); 98 ws_stream.close(None).await.unwrap();
82 99
83 let v_pb = add_pb(pb, DEFAULT_STYLE, "verify response".to_string()); 100 let v_pb = add_pb(pb, DEFAULT_STYLE, "verify response".to_string());
84 let res = verify_response(msg.unwrap().to_string(), uuid)?; 101 let res = verify_response(&msg.unwrap().to_string(), &uuid)?;
85 match res { 102 match res {
86 Verified::WrongUuid => { 103 Verified::WrongUuid => {
87 finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); 104 finish_pb(&v_pb, "returned wrong uuid".to_string(), ERROR_STYLE);
88 Ok(false) 105 Ok(false)
89 },
90 Verified::ResponseType(res_type) => {
91 match res_type {
92 ResponseType::Start => {
93 finish_pb(v_pb, "device started".to_string(), DONE_STYLE);
94 Ok(true)
95 },
96 ResponseType::Timeout => {
97 finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE);
98 Ok(false)
99 },
100 ResponseType::NotFound => {
101 finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE);
102 Ok(false)
103 },
104 }
105 } 106 }
107 Verified::ResponseType(res_type) => match res_type {
108 ResponseType::Start => {
109 finish_pb(&v_pb, "device started".to_string(), DONE_STYLE);
110 Ok(true)
111 }
112 ResponseType::Timeout => {
113 finish_pb(&v_pb, "ping timed out".to_string(), ERROR_STYLE);
114 Ok(false)
115 }
116 ResponseType::NotFound => {
117 finish_pb(&v_pb, "unknown uuid".to_string(), ERROR_STYLE);
118 Ok(false)
119 }
120 },
106 } 121 }
107} 122}
108 123
109fn get_eta(msg: String, uuid: String) -> Result<u64, CliError> { 124fn get_eta(msg: &str, uuid: &str) -> Result<String, Error> {
110 let spl: Vec<&str> = msg.split('_').collect(); 125 let spl: Vec<&str> = msg.split('_').collect();
111 if (spl[0] != "eta") || (spl[2] != uuid) { return Err(CliError::WsResponse); }; 126 if (spl[0] != "eta") || (spl[2] != uuid) {
112 Ok(u64::from_str_radix(spl[1], 10).map_err(CliError::Parse)?) 127 return Err(Error::WsResponse);
128 };
129 let input: u64 = spl[1].parse()?;
130
131 let sec = input % 60;
132 let min = (input / 60) % 60;
133 let hou = (input / (60 * 60)) % 60;
134
135 Ok(format!("{hou:0>2}:{min:0>2}:{sec:0>2}"))
113} 136}
114 137
115fn verify_response(res: String, org_uuid: String) -> Result<Verified, CliError> { 138fn verify_response(res: &str, org_uuid: &str) -> Result<Verified, Error> {
116 let spl: Vec<&str> = res.split('_').collect(); 139 let spl: Vec<&str> = res.split('_').collect();
117 let res_type = spl[0]; 140 let res_type = spl[0];
118 let uuid = spl[1]; 141 let uuid = spl[1];
119 142
120 if uuid != org_uuid { return Ok(Verified::WrongUuid) }; 143 if uuid != org_uuid {
121 144 return Ok(Verified::WrongUuid);
145 };
146
122 Ok(Verified::ResponseType(ResponseType::from(res_type)?)) 147 Ok(Verified::ResponseType(ResponseType::from(res_type)?))
123} 148}
124 149
@@ -131,7 +156,7 @@ struct StartResponse {
131 156
132enum Verified { 157enum Verified {
133 ResponseType(ResponseType), 158 ResponseType(ResponseType),
134 WrongUuid 159 WrongUuid,
135} 160}
136 161
137enum ResponseType { 162enum ResponseType {
@@ -141,12 +166,12 @@ enum ResponseType {
141} 166}
142 167
143impl ResponseType { 168impl ResponseType {
144 fn from(value: &str) -> Result<Self, CliError> { 169 fn from(value: &str) -> Result<Self, Error> {
145 match value { 170 match value {
146 "start" => Ok(ResponseType::Start), 171 "start" => Ok(ResponseType::Start),
147 "timeout" => Ok(ResponseType::Timeout), 172 "timeout" => Ok(ResponseType::Timeout),
148 "notfound" => Ok(ResponseType::NotFound), 173 "notfound" => Ok(ResponseType::NotFound),
149 _ => Err(CliError::WsResponse), 174 _ => Err(Error::WsResponse),
150 } 175 }
151 } 176 }
152} 177}