@smbcloud / cli / commits / d0b89e7

WIP

Seto Elkahfi committed Apr 13, 2023 at 20:38 UTC d0b89e7317fd82febbc073ae27ff73882fd05707
4 files changed +125 -1
Cargo.lock
+10
@@ -1060,6 +1060,7 @@ dependencies = [
1060 "reqwest",
1061 "serde",
1062 "serde_json",
1063 + "smbpndk-networking",
1064 "spinners",
1065 "tokio",
1066 "tracing",
@@ -1068,6 +1069,15 @@ dependencies = [
1069 "url-builder",
1070 ]
1071
1072 +[[package]]
1073 +name = "smbpndk-networking"
1074 +version = "0.0.1"
1075 +dependencies = [
1076 + "reqwest",
1077 + "serde",
1078 + "serde_json",
1079 +]
1080 +
1081 [[package]]
1082 name = "socket2"
1083 version = "0.4.7"
Cargo.toml
+7 -1
@@ -1,3 +1,8 @@
1 +[workspace]
2 +members = [
3 + "smbpndk-networking"
4 +]
5 +
6 [package]
7 name = "smbpndk-cli"
8 version = "0.2.1"
@@ -16,17 +21,18 @@ path = "src/main.rs"
21 [dependencies]
22 anyhow = "1.0.58"
23 clap = { version = "4.1.6", features = ["derive"] }
24 +clap_mangen = "0.2.10"
25 console = "0.15.0"
26 dialoguer = "0.10.4"
27 dirs = "4.0.0"
28 dotenv = "0.15.0"
23 -clap_mangen = "0.2.10"
29 home = "0.5.4"
30 open = "4.0.1"
31 regex = "1.3.1"
32 reqwest = { version = "0.11", features = ["json"] }
33 serde = { version = "1", features = ["derive"] }
34 serde_json = "1.0.82"
35 +smbpndk-networking = { path = "smbpndk-networking" }
36 spinners = "4.1.0"
37 tokio = { version = "1.14.1", features = ["full"] }
38 tracing = { version = "0.1.35", features = ["log"] }
smbpndk-networking/Cargo.toml new
+9
@@ -0,0 +1,9 @@
1 +[package]
2 +name = "smbpndk-networking"
3 +version = "0.0.1"
4 +edition = "2021"
5 +
6 +[dependencies]
7 +reqwest = { version = "0.11", features = ["json"] }
8 +serde = { version = "1", features = ["derive"] }
9 +serde_json = "1.0.82"
\ No newline at end of file
smbpndk-networking/src/lib.rs new
+99
@@ -0,0 +1,99 @@
1 +use serde_json::Value;
2 +use std::collections::HashMap;
3 +use std::time::Duration;
4 +use reqwest::Method;
5 +use reqwest::RequestBuilder;
6 +
7 +pub type Headers = HashMap<String, String>;
8 +pub type Query<'a> = HashMap<&'a str, &'a str>;
9 +
10 +pub trait HttpClient: Send + Default {
11 + type Error;
12 +
13 + fn get(
14 + &self,
15 + url: &str,
16 + headers: Option<&Headers>,
17 + payload: &Query,
18 + ) -> Result<String, Self::Error>;
19 +
20 + fn post(
21 + &self,
22 + url: &str,
23 + headers: Option<&Headers>,
24 + payload: &Value,
25 + ) -> Result<String, Self::Error>;
26 +}
27 +
28 +#[derive(thiserror::Error, Debug)]
29 +pub enum ReqwestError {
30 + /// The request couldn't be completed because there was an error when trying
31 + /// to do so
32 + #[error("request: {0}")]
33 + Client(#[from] reqwest::Error),
34 +
35 + /// The request was made, but the server returned an unsuccessful status
36 + /// code, such as 404 or 503. In some cases, the response may contain a
37 + /// custom message from Spotify with more information, which can be
38 + /// serialized into `rspotify_model::ApiError`.
39 + #[error("status code {}", reqwest::Response::status(.0))]
40 + StatusCode(reqwest::Response),
41 +}
42 +
43 +pub struct ReqwestClient {
44 + // An instance of reqwest::Client.
45 + client: reqwest::Client,
46 +}
47 +
48 +impl Default for ReqwestClient {
49 + fn default() -> Self {
50 + let client = reqwest::ClientBuilder::new()
51 + .user_agent("smbpndk-cli")
52 + .timeout(Duration::from_secs(10))
53 + .build()
54 + .unwrap();
55 + Self { client }
56 + }
57 +}
58 +
59 +impl ReqwestClient {
60 + async fn request<D>(
61 + &self,
62 + method: Method,
63 + url: &str,
64 + headers: Option<&Headers>,
65 + add_data: D,
66 + ) -> Result<String, ReqwestError>
67 + where
68 + D: Fn(RequestBuilder) -> RequestBuilder,
69 + {
70 + let mut request = self.client.request(method.clone(), url);
71 +
72 + // Setting the headers, if any
73 + if let Some(headers) = headers {
74 + // The headers need to be converted into a `reqwest::HeaderMap`,
75 + // which won't fail as long as its contents are ASCII. This is an
76 + // internal function, so the condition cannot be broken by the user
77 + // and will always be true.
78 + //
79 + // The content-type header will be set automatically.
80 + let headers = headers.try_into().unwrap();
81 +
82 + request = request.headers(headers);
83 + }
84 +
85 + // Configuring the request for the specific type (get/post/put/delete)
86 + request = add_data(request);
87 +
88 + // Finally performing the request and handling the response
89 + log::info!("Making request {:?}", request);
90 + let response = request.send().await?;
91 +
92 + // Making sure that the status code is OK
93 + if response.status().is_success() {
94 + response.text().await.map_err(Into::into)
95 + } else {
96 + Err(ReqwestError::StatusCode(response))
97 + }
98 + }
99 +}
\ No newline at end of file