summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFxQnLr <[email protected]>2024-02-25 16:14:56 +0100
committerFxQnLr <[email protected]>2024-02-25 16:14:56 +0100
commit03bea24f9de698375033af92a08762446d0e20cc (patch)
tree71b696ddcdc14f36115155be7f287fc0cb43e16d /src
parentcd73d51fba4a7d24b5ef12cb7d23054ab922cb67 (diff)
downloadwebol-cli-03bea24f9de698375033af92a08762446d0e20cc.tar
webol-cli-03bea24f9de698375033af92a08762446d0e20cc.tar.gz
webol-cli-03bea24f9de698375033af92a08762446d0e20cc.zip
Closes #2. Config and setup stuff
Diffstat (limited to 'src')
-rw-r--r--src/config.rs30
-rw-r--r--src/error.rs10
-rw-r--r--src/main.rs84
-rw-r--r--src/requests.rs (renamed from src/requests/mod.rs)0
-rw-r--r--src/requests/device.rs62
-rw-r--r--src/requests/start.rs103
6 files changed, 156 insertions, 133 deletions
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 @@
1use config::Config; 1use serde::Deserialize;
2use once_cell::sync::Lazy;
3 2
4pub static SETTINGS: Lazy<Config> = Lazy::new(setup); 3#[derive(Deserialize)]
5 4pub struct Config {
6fn setup() -> Config { 5 pub apikey: String,
7 #[cfg(not(debug_assertions))] 6 pub server: String,
8 let builder = Config::builder().add_source(config::File::with_name( 7}
9 format!("{}/webol-cli.toml", dirs::config_dir().unwrap().to_string_lossy()).as_str(),
10 ));
11 8
12 #[cfg(debug_assertions)] 9impl Config {
13 let builder = Config::builder().add_source(config::File::with_name("webol-cli.toml")); 10 pub fn load() -> Result<Config, config::ConfigError> {
11 let builder = config::Config::builder()
12 .add_source(config::File::with_name("~/.config/webol-cli.toml"))
13 .add_source(config::File::with_name("webol-cli.toml"))
14 .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_"))
15 .build()?;
14 16
15 builder 17 builder.try_deserialize()
16 .add_source(config::Environment::with_prefix("WEBOL_CLI_").separator("_")) 18 }
17 .build()
18 .unwrap()
19} 19}
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 {
11impl Debug for CliError { 11impl Debug for CliError {
12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 12 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
13 match self { 13 match self {
14 Self::Reqwest(err) => { err.fmt(f) }, 14 Self::Reqwest(err) => err.fmt(f),
15 Self::Config(err) => { err.fmt(f) }, 15 Self::Config(err) => err.fmt(f),
16 Self::Serde(err) => { err.fmt(f) }, 16 Self::Serde(err) => err.fmt(f),
17 Self::Parse(err) => { err.fmt(f) }, 17 Self::Parse(err) => err.fmt(f),
18 Self::WsResponse => { f.write_str("Error in Response") }, 18 Self::WsResponse => f.write_str("Error in Response"),
19 } 19 }
20 } 20 }
21} 21}
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 @@
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::CliError;
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::header::{HeaderMap, HeaderValue};
10use serde::Deserialize; 10use serde::Deserialize;
11 11
@@ -35,7 +35,7 @@ enum Commands {
35 /// id of the device 35 /// id of the device
36 id: String, 36 id: String,
37 #[arg(short, long)] 37 #[arg(short, long)]
38 ping: Option<bool> 38 ping: Option<bool>,
39 }, 39 },
40 Device { 40 Device {
41 #[command(subcommand)] 41 #[command(subcommand)]
@@ -52,7 +52,7 @@ enum DeviceCmd {
52 id: String, 52 id: String,
53 mac: String, 53 mac: String,
54 broadcast_addr: String, 54 broadcast_addr: String,
55 ip: String 55 ip: String,
56 }, 56 },
57 Get { 57 Get {
58 id: String, 58 id: String,
@@ -61,29 +61,39 @@ enum DeviceCmd {
61 id: String, 61 id: String,
62 mac: String, 62 mac: String,
63 broadcast_addr: String, 63 broadcast_addr: String,
64 ip: String 64 ip: String,
65 }, 65 },
66} 66}
67 67
68#[tokio::main] 68#[tokio::main]
69async fn main() -> Result<(), CliError> { 69async fn main() -> Result<(), CliError> {
70 let config = Config::load().map_err(CliError::Config)?;
71
70 let cli = Args::parse(); 72 let cli = Args::parse();
71 73
72 match cli.commands { 74 match cli.commands {
73 Commands::Start { id, ping } => { 75 Commands::Start { id, ping } => {
74 start(id, ping.unwrap_or(true)).await?; 76 start(&config, id, ping.unwrap_or(true)).await?;
75 }, 77 }
76 Commands::Device { devicecmd } => { 78 Commands::Device { devicecmd } => match devicecmd {
77 match devicecmd { 79 DeviceCmd::Add {
78 DeviceCmd::Add { id, mac, broadcast_addr, ip } => { 80 id,
79 device::put(id, mac, broadcast_addr, ip).await?; 81 mac,
80 }, 82 broadcast_addr,
81 DeviceCmd::Get { id } => { 83 ip,
82 device::get(id).await?; 84 } => {
83 }, 85 device::put(&config, id, mac, broadcast_addr, ip).await?;
84 DeviceCmd::Edit { id, mac, broadcast_addr, ip } => { 86 }
85 device::post(id, mac, broadcast_addr, ip).await?; 87 DeviceCmd::Get { id } => {
86 }, 88 device::get(&config, id).await?;
89 }
90 DeviceCmd::Edit {
91 id,
92 mac,
93 broadcast_addr,
94 ip,
95 } => {
96 device::post(&config, id, mac, broadcast_addr, ip).await?;
87 } 97 }
88 }, 98 },
89 Commands::CliGen { id } => { 99 Commands::CliGen { id } => {
@@ -100,29 +110,26 @@ fn print_completions<G: Generator>(gen: G, cmd: &mut Command) {
100 generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout()); 110 generate(gen, cmd, cmd.get_name().to_string(), &mut std::io::stdout());
101} 111}
102 112
103fn default_headers() -> Result<HeaderMap, CliError> { 113fn default_headers(config: &Config) -> Result<HeaderMap, CliError> {
104 let mut map = HeaderMap::new(); 114 let mut map = HeaderMap::new();
105 map.append("Accept-Content", HeaderValue::from_str("application/json").unwrap()); 115 map.append(
106 map.append("Content-Type", HeaderValue::from_str("application/json").unwrap()); 116 "Accept-Content",
117 HeaderValue::from_str("application/json").unwrap(),
118 );
119 map.append(
120 "Content-Type",
121 HeaderValue::from_str("application/json").unwrap(),
122 );
107 map.append( 123 map.append(
108 "Authorization", 124 "Authorization",
109 HeaderValue::from_str( 125 HeaderValue::from_str(&config.apikey).unwrap(),
110 SETTINGS.get_string("key")
111 .map_err(CliError::Config)?
112 .as_str()
113 ).unwrap()
114 ); 126 );
115 127
116 Ok(map) 128 Ok(map)
117} 129}
118 130
119fn format_url(path: &str, protocol: Protocols) -> Result<String, CliError> { 131fn format_url(config: &Config, path: &str, protocol: Protocols) -> Result<String, CliError> {
120 Ok(format!( 132 Ok(format!("{}://{}/{}", protocol, config.server, path))
121 "{}://{}/{}",
122 protocol,
123 SETTINGS.get_string("server").map_err(CliError::Config)?,
124 path
125 ))
126} 133}
127 134
128fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar { 135fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar {
@@ -137,7 +144,6 @@ fn add_pb(mp: &MultiProgress, template: &str, message: String) -> ProgressBar {
137fn finish_pb(pb: ProgressBar, message: String, template: &str) { 144fn finish_pb(pb: ProgressBar, message: String, template: &str) {
138 pb.set_style(ProgressStyle::with_template(template).unwrap()); 145 pb.set_style(ProgressStyle::with_template(template).unwrap());
139 pb.finish_with_message(message); 146 pb.finish_with_message(message);
140
141} 147}
142 148
143enum Protocols { 149enum Protocols {
@@ -149,12 +155,12 @@ impl Display for Protocols {
149 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 155 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150 match self { 156 match self {
151 Self::Http => f.write_str("http"), 157 Self::Http => f.write_str("http"),
152 Self::Websocket => f.write_str("ws") 158 Self::Websocket => f.write_str("ws"),
153 } 159 }
154 } 160 }
155} 161}
156 162
157#[derive(Debug, Deserialize)] 163#[derive(Debug, Deserialize)]
158struct ErrorResponse { 164struct ErrorResponse {
159 error: String 165 error: String,
160} 166}
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..5003c4a 100644
--- a/src/requests/device.rs
+++ b/src/requests/device.rs
@@ -1,20 +1,21 @@
1use crate::{error::CliError, default_headers, format_url, Protocols}; 1use crate::{config::Config, default_headers, error::CliError, 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 id: String,
6 mac: String,
7 broadcast_addr: String,
8 ip: String,
9) -> Result<(), CliError> {
10 let url = format_url(config, "device", Protocols::Http)?;
5 println!("{}", url); 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": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#,
11 r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, 17 id, mac, broadcast_addr, ip
12 id, 18 ))
13 mac,
14 broadcast_addr,
15 ip
16 )
17 )
18 .send() 19 .send()
19 .await 20 .await
20 .map_err(CliError::Reqwest)? 21 .map_err(CliError::Reqwest)?
@@ -25,13 +26,11 @@ pub async fn put(id: String, mac: String, broadcast_addr: String, ip: String) ->
25 Ok(()) 26 Ok(())
26} 27}
27 28
28pub async fn get(id: String) -> Result<(), CliError> { 29pub async fn get(config: &Config, id: String) -> Result<(), CliError> {
29 let res = reqwest::Client::new() 30 let res = reqwest::Client::new()
30 .get(format_url("device", Protocols::Http)?) 31 .get(format_url(config, "device", Protocols::Http)?)
31 .headers(default_headers()?) 32 .headers(default_headers(config)?)
32 .body( 33 .body(format!(r#"{{"id": "{}"}}"#, id))
33 format!(r#"{{"id": "{}"}}"#, id)
34 )
35 .send() 34 .send()
36 .await 35 .await
37 .map_err(CliError::Reqwest)? 36 .map_err(CliError::Reqwest)?
@@ -42,19 +41,20 @@ pub async fn get(id: String) -> Result<(), CliError> {
42 Ok(()) 41 Ok(())
43} 42}
44 43
45pub async fn post(id: String, mac: String, broadcast_addr: String, ip: String) -> Result<(), CliError> { 44pub async fn post(
45 config: &Config,
46 id: String,
47 mac: String,
48 broadcast_addr: String,
49 ip: String,
50) -> Result<(), CliError> {
46 let res = reqwest::Client::new() 51 let res = reqwest::Client::new()
47 .post(format_url("device", Protocols::Http)?) 52 .post(format_url(config, "device", Protocols::Http)?)
48 .headers(default_headers()?) 53 .headers(default_headers(config)?)
49 .body( 54 .body(format!(
50 format!( 55 r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#,
51 r#"{{"id": "{}", "mac": "{}", "broadcast_addr": "{}", "ip": "{}"}}"#, 56 id, mac, broadcast_addr, ip
52 id, 57 ))
53 mac,
54 broadcast_addr,
55 ip
56 )
57 )
58 .send() 58 .send()
59 .await 59 .await
60 .map_err(CliError::Reqwest)? 60 .map_err(CliError::Reqwest)?
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 @@
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::{connect_async, tungstenite::Message};
6 6
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}; 7use crate::{
8 8 add_pb, config::Config, default_headers, error::CliError, finish_pb, format_url, ErrorResponse,
9pub async fn start(id: String, ping: bool) -> Result<(), CliError> { 9 Protocols, DEFAULT_STYLE, DONE_STYLE, ERROR_STYLE, OVERVIEW_DONE, OVERVIEW_ERROR,
10 OVERVIEW_STYLE,
11};
10 12
13pub async fn start(config: &Config, id: String, ping: bool) -> Result<(), CliError> {
11 let send_start = MultiProgress::new(); 14 let send_start = MultiProgress::new();
12 let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {}", id)); 15 let overview = add_pb(&send_start, OVERVIEW_STYLE, format!(") start {}", id));
13 16
14 // TODO: calculate average start-time on server 17 // TODO: calculate average start-time on server
15 let url = format_url("start", Protocols::Http)?; 18 let url = format_url(config, "start", Protocols::Http)?;
16 let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url)); 19 let connect = add_pb(&send_start, DEFAULT_STYLE, format!("connect to {}", url));
17 let res = reqwest::Client::new() 20 let res = reqwest::Client::new()
18 .post(url) 21 .post(url)
19 .headers(default_headers()?) 22 .headers(default_headers(config)?)
20 .body( 23 .body(format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping))
21 format!(r#"{{"id": "{}", "ping": {}}}"#, id, ping)
22 )
23 .send() 24 .send()
24 .await 25 .await
25 .map_err(CliError::Reqwest)?; 26 .map_err(CliError::Reqwest)?;
@@ -29,7 +30,7 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> {
29 match res.status() { 30 match res.status() {
30 StatusCode::OK => { 31 StatusCode::OK => {
31 let body = serde_json::from_str::<StartResponse>( 32 let body = serde_json::from_str::<StartResponse>(
32 &res.text().await.map_err(CliError::Reqwest)? 33 &res.text().await.map_err(CliError::Reqwest)?,
33 ) 34 )
34 .map_err(CliError::Serde)?; 35 .map_err(CliError::Serde)?;
35 36
@@ -38,17 +39,25 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> {
38 } 39 }
39 40
40 if ping { 41 if ping {
41 let status = status_socket(body.uuid, &send_start, &overview, id).await?; 42 let status = status_socket(config, body.uuid, &send_start, &overview, id).await?;
42 if status { 43 if status {
43 finish_pb(overview, format!("successfully started {}", body.id), OVERVIEW_DONE); 44 finish_pb(
45 overview,
46 format!("successfully started {}", body.id),
47 OVERVIEW_DONE,
48 );
44 } else { 49 } else {
45 finish_pb(overview, format!("error while starting {}", body.id), OVERVIEW_ERROR); 50 finish_pb(
51 overview,
52 format!("error while starting {}", body.id),
53 OVERVIEW_ERROR,
54 );
46 } 55 }
47 } 56 }
48 }, 57 }
49 _ => { 58 _ => {
50 let body = serde_json::from_str::<ErrorResponse>( 59 let body = serde_json::from_str::<ErrorResponse>(
51 &res.text().await.map_err(CliError::Reqwest)? 60 &res.text().await.map_err(CliError::Reqwest)?,
52 ) 61 )
53 .map_err(CliError::Serde)?; 62 .map_err(CliError::Serde)?;
54 63
@@ -59,16 +68,22 @@ pub async fn start(id: String, ping: bool) -> Result<(), CliError> {
59 Ok(()) 68 Ok(())
60} 69}
61 70
62async fn status_socket(uuid: String, pb: &MultiProgress, overview: &ProgressBar, id: String) -> Result<bool, CliError> { 71async fn status_socket(
63 // TODO: Remove unwraps 72 config: &Config,
73 uuid: String,
74 pb: &MultiProgress,
75 overview: &ProgressBar,
76 id: String,
77) -> Result<bool, CliError> {
64 let ws_pb = add_pb(pb, DEFAULT_STYLE, "connect to websocket".to_string()); 78 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)?) 79 let (mut ws_stream, _response) =
66 .await 80 connect_async(format_url(config, "status", Protocols::Websocket)?)
67 .expect("Failed to connect"); 81 .await
82 .expect("Failed to connect");
68 finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE); 83 finish_pb(ws_pb, "connected to websocket".to_string(), DONE_STYLE);
69 84
70 ws_stream.send(Message::Text(uuid.clone())).await.unwrap(); 85 ws_stream.send(Message::Text(uuid.clone())).await.unwrap();
71 86
72 // Get ETA 87 // Get ETA
73 let eta_msg = ws_stream.next().await.unwrap().unwrap(); 88 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(); 89 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,
86 Verified::WrongUuid => { 101 Verified::WrongUuid => {
87 finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE); 102 finish_pb(v_pb, "returned wrong uuid".to_string(), ERROR_STYLE);
88 Ok(false) 103 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 } 104 }
105 Verified::ResponseType(res_type) => match res_type {
106 ResponseType::Start => {
107 finish_pb(v_pb, "device started".to_string(), DONE_STYLE);
108 Ok(true)
109 }
110 ResponseType::Timeout => {
111 finish_pb(v_pb, "ping timed out".to_string(), ERROR_STYLE);
112 Ok(false)
113 }
114 ResponseType::NotFound => {
115 finish_pb(v_pb, "unknown uuid".to_string(), ERROR_STYLE);
116 Ok(false)
117 }
118 },
106 } 119 }
107} 120}
108 121
109fn get_eta(msg: String, uuid: String) -> Result<u64, CliError> { 122fn get_eta(msg: String, uuid: String) -> Result<u64, CliError> {
110 let spl: Vec<&str> = msg.split('_').collect(); 123 let spl: Vec<&str> = msg.split('_').collect();
111 if (spl[0] != "eta") || (spl[2] != uuid) { return Err(CliError::WsResponse); }; 124 if (spl[0] != "eta") || (spl[2] != uuid) {
125 return Err(CliError::WsResponse);
126 };
112 Ok(u64::from_str_radix(spl[1], 10).map_err(CliError::Parse)?) 127 Ok(u64::from_str_radix(spl[1], 10).map_err(CliError::Parse)?)
113} 128}
114 129
@@ -116,9 +131,11 @@ fn verify_response(res: String, org_uuid: String) -> Result<Verified, CliError>
116 let spl: Vec<&str> = res.split('_').collect(); 131 let spl: Vec<&str> = res.split('_').collect();
117 let res_type = spl[0]; 132 let res_type = spl[0];
118 let uuid = spl[1]; 133 let uuid = spl[1];
119 134
120 if uuid != org_uuid { return Ok(Verified::WrongUuid) }; 135 if uuid != org_uuid {
121 136 return Ok(Verified::WrongUuid);
137 };
138
122 Ok(Verified::ResponseType(ResponseType::from(res_type)?)) 139 Ok(Verified::ResponseType(ResponseType::from(res_type)?))
123} 140}
124 141
@@ -131,7 +148,7 @@ struct StartResponse {
131 148
132enum Verified { 149enum Verified {
133 ResponseType(ResponseType), 150 ResponseType(ResponseType),
134 WrongUuid 151 WrongUuid,
135} 152}
136 153
137enum ResponseType { 154enum ResponseType {