1#![allow(clippy::too_long_first_doc_paragraph)]
2
3use url::Url;
4
5mod clients;
6mod errors;
7pub mod types;
8
9pub use clients::*;
10pub use errors::*;
11
12pub struct ClientBuilder {
14 api_key: String,
16
17 api_url: Option<Url>,
19 reqwest_client: Option<reqwest::Client>,
20}
21
22impl ClientBuilder {
24 pub fn new(api_key: String) -> ClientBuilder {
26 ClientBuilder {
27 api_key,
28 api_url: None,
29 reqwest_client: None,
30 }
31 }
32
33 pub fn api_url(&mut self, api_url: Url) -> &mut Self {
35 self.api_url = Some(api_url);
36 self
37 }
38
39 pub fn reqwest_client(&mut self, client: reqwest::Client) -> &mut Self {
41 self.reqwest_client = Some(client);
42 self
43 }
44
45 pub fn build(self) -> Client {
47 Client {
48 api_key: self.api_key,
49 api_url: self
50 .api_url
51 .unwrap_or_else(|| Url::parse("https://api.ngrok.com").unwrap()),
52 c: self.reqwest_client.unwrap_or_default(),
53 }
54 }
55}
56
57#[derive(Clone, Debug)]
59pub struct Client {
60 api_key: String,
62 api_url: Url,
64 c: reqwest::Client,
65}
66
67impl Client {
68 pub fn new(api_key: String) -> Self {
79 ClientBuilder::new(api_key).build()
80 }
81
82 pub(crate) async fn make_request<T, R>(
83 &self,
84 path: &str,
85 method: reqwest::Method,
86 req: Option<T>,
87 ) -> Result<R, Error>
88 where
89 T: serde::Serialize,
90 R: serde::de::DeserializeOwned + Default,
91 {
92 let api_url = &self.api_url;
93
94 let mut builder = self
95 .c
96 .request(method.clone(), api_url.join(path).unwrap())
97 .bearer_auth(&self.api_key)
98 .header("Ngrok-Version", "2");
99 if let Some(r) = req {
100 builder = match method {
102 reqwest::Method::GET => builder.query(&r),
103 _ => builder.json(&r),
104 };
105 }
106
107 let resp = builder.send().await?;
108
109 match resp.status() {
110 reqwest::StatusCode::NO_CONTENT => return Ok(Default::default()),
111 s if s.is_success() => {
112 return resp.json().await.map_err(|e| e.into());
113 }
114 _ => {}
115 }
116
117 let resp_bytes = resp.bytes().await?;
120 if let Ok(e) = serde_json::from_slice(&resp_bytes) {
121 return Err(Error::Ngrok(e));
123 }
124 Err(Error::UnknownError(
126 String::from_utf8_lossy(&resp_bytes).into(),
127 ))
128 }
129
130 pub(crate) async fn get_by_uri<R>(&self, uri: &str) -> Result<R, Error>
131 where
132 R: serde::de::DeserializeOwned,
133 {
134 let builder = self
135 .c
136 .request(reqwest::Method::GET, uri)
137 .bearer_auth(&self.api_key)
138 .header("Ngrok-Version", "2");
139
140 let resp = builder.send().await?;
141 if resp.status().is_success() {
142 return resp.json().await.map_err(|e| e.into());
143 }
144 let resp_bytes = resp.bytes().await?;
145 if let Ok(e) = serde_json::from_slice(&resp_bytes) {
146 return Err(Error::Ngrok(e));
147 }
148 Err(Error::UnknownError(
149 String::from_utf8_lossy(&resp_bytes).into(),
150 ))
151 }
152}