diff options
-rw-r--r-- | .gitignore | 1 | ||||
-rw-r--r-- | src/error.rs | 2 | ||||
-rw-r--r-- | src/main.rs | 28 | ||||
-rw-r--r-- | src/requests/device.rs | 4 | ||||
-rw-r--r-- | src/requests/start.rs | 113 | ||||
-rw-r--r-- | webol-cli.toml | 2 |
6 files changed, 112 insertions, 38 deletions
@@ -1,2 +1,3 @@ | |||
1 | /target | 1 | /target |
2 | /postgres-data | 2 | /postgres-data |
3 | webol-cli.toml | ||
diff --git a/src/error.rs b/src/error.rs index d35991b..27fc7a6 100644 --- a/src/error.rs +++ b/src/error.rs | |||
@@ -4,6 +4,7 @@ pub enum CliError { | |||
4 | Reqwest(reqwest::Error), | 4 | Reqwest(reqwest::Error), |
5 | Config(config::ConfigError), | 5 | Config(config::ConfigError), |
6 | Serde(serde_json::Error), | 6 | Serde(serde_json::Error), |
7 | WsResponse, | ||
7 | } | 8 | } |
8 | 9 | ||
9 | impl Debug for CliError { | 10 | impl Debug for CliError { |
@@ -12,6 +13,7 @@ impl Debug for CliError { | |||
12 | Self::Reqwest(err) => { err.fmt(f) }, | 13 | Self::Reqwest(err) => { err.fmt(f) }, |
13 | Self::Config(err) => { err.fmt(f) }, | 14 | Self::Config(err) => { err.fmt(f) }, |
14 | Self::Serde(err) => { err.fmt(f) }, | 15 | Self::Serde(err) => { err.fmt(f) }, |
16 | Self::WsResponse => { f.write_str("Error in Response") }, | ||
15 | } | 17 | } |
16 | } | 18 | } |
17 | } | 19 | } |
diff --git a/src/main.rs b/src/main.rs index 3e1388b..d7c985f 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,8 +1,9 @@ | |||
1 | use std::fmt::Display; | 1 | use std::{fmt::Display, time::Duration}; |
2 | 2 | ||
3 | use clap::{Parser, Subcommand}; | 3 | use clap::{Parser, Subcommand}; |
4 | use config::SETTINGS; | 4 | use config::SETTINGS; |
5 | use error::CliError; | 5 | use error::CliError; |
6 | use indicatif::{ProgressBar, ProgressStyle, MultiProgress}; | ||
6 | use requests::{start::start, device}; | 7 | use requests::{start::start, device}; |
7 | use reqwest::header::{HeaderMap, HeaderValue}; | 8 | use reqwest::header::{HeaderMap, HeaderValue}; |
8 | use serde::Deserialize; | 9 | use serde::Deserialize; |
@@ -11,7 +12,15 @@ mod config; | |||
11 | mod error; | 12 | mod error; |
12 | mod requests; | 13 | mod requests; |
13 | 14 | ||
14 | /// webol http client | 15 | static OVERVIEW_STYLE: &str = "{spinner:.green} {wide_msg}({elapsed})"; |
16 | static OVERVIEW_ERROR: &str = "✗ {wide_msg}({elapsed})"; | ||
17 | static OVERVIEW_DONE: &str = "✓ {wide_msg}({elapsed})"; | ||
18 | static DEFAULT_STYLE: &str = " {spinner:.green} {wide_msg}"; | ||
19 | static DONE_STYLE: &str = " ✓ {wide_msg}"; | ||
20 | static ERROR_STYLE: &str = " ✗ {wide_msg}"; | ||
21 | static TICK_SPEED: u64 = 1000 / 16; | ||
22 | |||
23 | /// webol client | ||
15 | #[derive(Parser)] | 24 | #[derive(Parser)] |
16 | #[command(author, version, about, long_about = None)] | 25 | #[command(author, version, about, long_about = None)] |
17 | struct Args { | 26 | struct Args { |
@@ -103,6 +112,21 @@ fn format_url(path: &str, protocol: Protocols) -> Result<String, CliError> { | |||
103 | )) | 112 | )) |
104 | } | 113 | } |
105 | 114 | ||
115 | fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { | ||
116 | let pb = mp.add(ProgressBar::new(1)); | ||
117 | pb.set_style(ProgressStyle::with_template(template).unwrap()); | ||
118 | pb.enable_steady_tick(Duration::from_millis(TICK_SPEED)); | ||
119 | pb.set_message(message); | ||
120 | |||
121 | pb | ||
122 | } | ||
123 | |||
124 | fn finish_pb(pb: ProgressBar, message: String, template: &str) { | ||
125 | pb.set_style(ProgressStyle::with_template(template).unwrap()); | ||
126 | pb.finish_with_message(message); | ||
127 | |||
128 | } | ||
129 | |||
106 | enum Protocols { | 130 | enum Protocols { |
107 | Http, | 131 | Http, |
108 | Websocket, | 132 | Websocket, |
diff --git a/src/requests/device.rs b/src/requests/device.rs index f7754a4..cbc838e 100644 --- a/src/requests/device.rs +++ b/src/requests/device.rs | |||
@@ -1,8 +1,10 @@ | |||
1 | use crate::{error::CliError, default_headers, format_url, Protocols}; | 1 | use crate::{error::CliError, default_headers, format_url, Protocols}; |
2 | 2 | ||
3 | pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { | 3 | pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { |
4 | let url = format_url("device", Protocols::Http)?; | ||
5 | println!("{}", url); | ||
4 | let res = reqwest::Client::new() | 6 | let res = reqwest::Client::new() |
5 | .put(format_url("device", Protocols::Http)?) | 7 | .put(url) |
6 | .headers(default_headers()?) | 8 | .headers(default_headers()?) |
7 | .body( | 9 | .body( |
8 | format!( | 10 | format!( |
diff --git a/src/requests/start.rs b/src/requests/start.rs index d0c4411..882b154 100644 --- a/src/requests/start.rs +++ b/src/requests/start.rs | |||
@@ -1,29 +1,19 @@ | |||
1 | use std::time::Duration; | ||
2 | |||
3 | use futures_util::{StreamExt, SinkExt}; | 1 | use futures_util::{StreamExt, SinkExt}; |
4 | use indicatif::{ProgressBar, ProgressStyle}; | 2 | use indicatif::MultiProgress; |
5 | use reqwest::StatusCode; | 3 | use reqwest::StatusCode; |
6 | use serde::Deserialize; | 4 | use serde::Deserialize; |
7 | use tokio_tungstenite::{connect_async, tungstenite::Message}; | 5 | use tokio_tungstenite::{connect_async, tungstenite::Message}; |
8 | 6 | ||
9 | use crate::{error::CliError, default_headers, ErrorResponse, format_url, Protocols}; | 7 | 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}; |
10 | 8 | ||
11 | pub async fn start(id: String, ping: bool) -> Result<(), CliError> { | 9 | pub async fn start(id: String, ping: bool) -> Result<(), CliError> { |
12 | 10 | ||
13 | let send_start = ProgressBar::new(1); | 11 | let send_start = MultiProgress::new(); |
12 | let overview = add_pb(&send_start, OVERVIEW_STYLE, format!("start {}", id)); | ||
14 | 13 | ||
15 | // TODO: calculate average start-time on server | 14 | // TODO: calculate average start-time on server |
16 | send_start.set_style( | ||
17 | ProgressStyle::with_template("{spinner:.green} ({elapsed}) {wide_msg}") | ||
18 | .unwrap() | ||
19 | .tick_chars("|/-\\\\") | ||
20 | ); | ||
21 | |||
22 | let url = format_url("start", Protocols::Http)?; | 15 | let url = format_url("start", Protocols::Http)?; |
23 | 16 | let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url)); | |
24 | send_start.set_message(format!("connect to {}", url)); | ||
25 | send_start.enable_steady_tick(Duration::from_millis(125)); | ||
26 | |||
27 | let res = reqwest::Client::new() | 17 | let res = reqwest::Client::new() |
28 | .post(url) | 18 | .post(url) |
29 | .headers(default_headers()?) | 19 | .headers(default_headers()?) |
@@ -33,7 +23,9 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { | |||
33 | .send() | 23 | .send() |
34 | .await | 24 | .await |
35 | .map_err(CliError::Reqwest)?; | 25 | .map_err(CliError::Reqwest)?; |
26 | finish_pb(connect, "connected, got response".to_string(), DONE_STYLE); | ||
36 | 27 | ||
28 | let res_pb = add_pb(&send_start, DEFAULT_STYLE, "analyzing response".to_string()); | ||
37 | match res.status() { | 29 | match res.status() { |
38 | StatusCode::OK => { | 30 | StatusCode::OK => { |
39 | let body = serde_json::from_str::<StartResponse>( | 31 | let body = serde_json::from_str::<StartResponse>( |
@@ -42,11 +34,16 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { | |||
42 | .map_err(CliError::Serde)?; | 34 | .map_err(CliError::Serde)?; |
43 | 35 | ||
44 | if body.boot { | 36 | if body.boot { |
45 | send_start.println("connected, sent start packet"); | 37 | finish_pb(res_pb, "sent start packet".to_string(), DONE_STYLE); |
46 | } | 38 | } |
47 | 39 | ||
48 | if ping { | 40 | if ping { |
49 | send_start.println(status_socket(body.uuid, &send_start).await?.to_string()); | 41 | let status = status_socket(body.uuid, &send_start).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 | } | ||
50 | } | 47 | } |
51 | }, | 48 | }, |
52 | _ => { | 49 | _ => { |
@@ -55,37 +52,63 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> { | |||
55 | ) | 52 | ) |
56 | .map_err(CliError::Serde)?; | 53 | .map_err(CliError::Serde)?; |
57 | 54 | ||
58 | println!("got error: {}", body.error); | 55 | res_pb.finish_with_message(format!("✗ got error: {}", body.error)); |
59 | } | 56 | } |
60 | } | 57 | } |
61 | 58 | ||
62 | Ok(()) | 59 | Ok(()) |
63 | } | 60 | } |
64 | 61 | ||
65 | async fn status_socket(uuid: String, pb: &ProgressBar) -> Result<bool, CliError> { | 62 | async fn status_socket(uuid: String, pb: &MultiProgress) -> Result<bool, CliError> { |
66 | pb.set_message("setup websocket"); | 63 | // TODO: Remove unwraps |
67 | 64 | let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); | |
68 | let (mut ws_stream, _response) = connect_async(format_url("status", Protocols::Websocket)?) | 65 | let (mut ws_stream, _response) = connect_async(format_url("status", Protocols::Websocket)?) |
69 | .await | 66 | .await |
70 | .expect("Failed to connect"); | 67 | .expect("Failed to connect"); |
71 | pb.println("connected to websocket"); | 68 | finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE); |
72 | 69 | ||
73 | pb.set_message("send uuid message"); | 70 | ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); |
74 | ws_stream.send(Message::Text(uuid)).await.unwrap(); | ||
75 | pb.println("sent uuid message"); | ||
76 | 71 | ||
77 | pb.set_message("wait for message"); | 72 | let msg_pb = add_pb(pb, DEFAULT_STYLE, "await message".to_string()); |
78 | let msg = ws_stream.next().await.unwrap(); | 73 | let msg = ws_stream.next().await.unwrap(); |
74 | finish_pb(msg_pb, "received message".to_string(), DONE_STYLE); | ||
79 | 75 | ||
80 | pb.println(format!("msg: {:?}", msg)); | ||
81 | |||
82 | ws_stream.close(None).await.unwrap(); | 76 | ws_stream.close(None).await.unwrap(); |
83 | pb.println("connection closed"); | ||
84 | // TODO: Check for correct UUID and timeout | ||
85 | pb.set_message("verifying message"); | ||
86 | if msg.is_ok() { return Ok(true) } | ||
87 | 77 | ||
88 | Ok(false) | 78 | let v_pb = add_pb(pb, DEFAULT_STYLE, "verify response".to_string()); |
79 | let res = verify_response(msg.unwrap().to_string(), uuid)?; | ||
80 | match res { | ||
81 | Verified::WrongUuid => { | ||
82 | finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); | ||
83 | Ok(false) | ||
84 | }, | ||
85 | Verified::ResponseType(res_type) => { | ||
86 | match res_type { | ||
87 | ResponseType::Start => { | ||
88 | finish_pb(v_pb, "device started".to_string(), DONE_STYLE); | ||
89 | Ok(true) | ||
90 | }, | ||
91 | ResponseType::Timeout => { | ||
92 | finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE); | ||
93 | Ok(false) | ||
94 | }, | ||
95 | ResponseType::NotFound => { | ||
96 | finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE); | ||
97 | Ok(false) | ||
98 | }, | ||
99 | } | ||
100 | } | ||
101 | } | ||
102 | } | ||
103 | |||
104 | fn verify_response(res: String, org_uuid: String) -> Result<Verified, CliError> { | ||
105 | let spl: Vec<&str> = res.split('_').collect(); | ||
106 | let res_type = spl[0]; | ||
107 | let uuid = spl[1]; | ||
108 | |||
109 | if uuid != org_uuid { return Ok(Verified::WrongUuid) }; | ||
110 | |||
111 | Ok(Verified::ResponseType(ResponseType::from(res_type)?)) | ||
89 | } | 112 | } |
90 | 113 | ||
91 | #[derive(Debug, Deserialize)] | 114 | #[derive(Debug, Deserialize)] |
@@ -94,3 +117,25 @@ struct StartResponse { | |||
94 | id: String, | 117 | id: String, |
95 | uuid: String, | 118 | uuid: String, |
96 | } | 119 | } |
120 | |||
121 | enum Verified { | ||
122 | ResponseType(ResponseType), | ||
123 | WrongUuid | ||
124 | } | ||
125 | |||
126 | enum ResponseType { | ||
127 | Start, | ||
128 | Timeout, | ||
129 | NotFound, | ||
130 | } | ||
131 | |||
132 | impl ResponseType { | ||
133 | fn from(value: &str) -> Result<Self, CliError> { | ||
134 | match value { | ||
135 | "start" => Ok(ResponseType::Start), | ||
136 | "timeout" => Ok(ResponseType::Timeout), | ||
137 | "notfound" => Ok(ResponseType::NotFound), | ||
138 | _ => Err(CliError::WsResponse), | ||
139 | } | ||
140 | } | ||
141 | } | ||
diff --git a/webol-cli.toml b/webol-cli.toml index 7ffb99d..822af46 100644 --- a/webol-cli.toml +++ b/webol-cli.toml | |||
@@ -1,2 +1,2 @@ | |||
1 | server = "192.168.178.28:7229" | 1 | server = "localhost:7229" |
2 | key = "aaa" | 2 | key = "aaa" |