1mod api_error;
62mod create_instance_builder;
63mod data;
64mod error;
65
66use api_error::UpcloudApiErrorRoot;
67use data::{
68 UpcloudAccountRoot, UpcloudAccountsListRoot, UpcloudPlanListRoot, UpcloudPricesListRoot,
69 UpcloudServerListRoot, UpcloudServerRoot, UpcloudServerTemplateListRoot, UpcloudZoneListRoot,
70};
71use serde::Serialize;
72use serde_json::json;
73
74pub use create_instance_builder::CreateInstanceBuilder;
75pub use data::{
76 UpcloudAccount, UpcloudAccountsListItem, UpcloudLabel, UpcloudLabelList, UpcloudPlan,
77 UpcloudPrice, UpcloudPricesZone, UpcloudServer, UpcloudServerTemplate, UpcloudTagList,
78 UpcloudZone,
79};
80pub use error::UpcloudError;
81
82#[derive(Clone)]
83pub struct UpcloudApi {
84 username: String,
85 password: String,
86}
87
88impl<'a> UpcloudApi {
89 pub fn new<S1, S2>(username: S1, password: S2) -> UpcloudApi
90 where
91 S1: Into<String>,
92 S2: Into<String>,
93 {
94 UpcloudApi {
95 username: username.into(),
96 password: password.into(),
97 }
98 }
99
100 #[cfg(feature = "blocking")]
101 fn get(&self, url: &str) -> Result<reqwest::blocking::Response, UpcloudError> {
102 let client = reqwest::blocking::Client::new();
103 let resp = client
104 .get(url)
105 .basic_auth(&self.username, Some(&self.password))
106 .send()?;
107 let status = resp.status();
108 if status.is_client_error() {
109 let result: UpcloudApiErrorRoot = resp.json()?;
110 Err(UpcloudError::Api(result.error.error_message))
111 } else {
112 Ok(resp.error_for_status()?)
113 }
114 }
115
116 async fn get_async(&self, url: &str) -> Result<reqwest::Response, UpcloudError> {
117 let client = reqwest::Client::new();
118 let resp = client
119 .get(url)
120 .basic_auth(&self.username, Some(&self.password))
121 .send()
122 .await?;
123 let status = resp.status();
124 if status.is_client_error() {
125 let result: UpcloudApiErrorRoot = resp.json().await?;
126 Err(UpcloudError::Api(result.error.error_message))
127 } else {
128 Ok(resp.error_for_status()?)
129 }
130 }
131
132 #[cfg(feature = "blocking")]
133 fn post<T>(&self, url: &str, json: T) -> Result<reqwest::blocking::Response, UpcloudError>
134 where
135 T: Serialize + Sized,
136 {
137 let client = reqwest::blocking::Client::new();
138 let resp = client
139 .post(url)
140 .basic_auth(&self.username, Some(&self.password))
141 .json(&json)
142 .send()?;
143 let status = resp.status();
144 if status.is_client_error() {
145 let result: UpcloudApiErrorRoot = resp.json()?;
146 Err(UpcloudError::Api(result.error.error_message))
147 } else {
148 Ok(resp.error_for_status()?)
149 }
150 }
151
152 async fn post_async<T>(&self, url: &str, json: T) -> Result<reqwest::Response, UpcloudError>
153 where
154 T: Serialize + Sized,
155 {
156 let client = reqwest::Client::new();
157 let resp = client
158 .post(url)
159 .basic_auth(&self.username, Some(&self.password))
160 .json(&json)
161 .send()
162 .await?;
163 let status = resp.status();
164 if status.is_client_error() {
165 let result: UpcloudApiErrorRoot = resp.json().await?;
166 Err(UpcloudError::Api(result.error.error_message))
167 } else {
168 Ok(resp.error_for_status()?)
169 }
170 }
171
172 #[cfg(feature = "blocking")]
173 fn delete(&self, url: &str) -> Result<reqwest::blocking::Response, UpcloudError> {
174 let client = reqwest::blocking::Client::new();
175 let resp = client
176 .delete(url)
177 .basic_auth(&self.username, Some(&self.password))
178 .send()?;
179 let status = resp.status();
180 if status.is_client_error() {
181 let result: UpcloudApiErrorRoot = resp.json()?;
182 Err(UpcloudError::Api(result.error.error_message))
183 } else {
184 Ok(resp.error_for_status()?)
185 }
186 }
187
188 async fn delete_async(&self, url: &str) -> Result<reqwest::Response, UpcloudError> {
189 let client = reqwest::Client::new();
190 let resp = client
191 .delete(url)
192 .basic_auth(&self.username, Some(&self.password))
193 .send()
194 .await?;
195 let status = resp.status();
196 if status.is_client_error() {
197 let result: UpcloudApiErrorRoot = resp.json().await?;
198 Err(UpcloudError::Api(result.error.error_message))
199 } else {
200 Ok(resp.error_for_status()?)
201 }
202 }
203
204 #[cfg(feature = "blocking")]
205 pub fn get_account_info(&self) -> Result<UpcloudAccount, UpcloudError> {
206 Ok(self
207 .get("https://api.Upcloud.com/1.3/account")?
208 .json::<UpcloudAccountRoot>()?
209 .account)
210 }
211
212 pub async fn get_account_info_async(&self) -> Result<UpcloudAccount, UpcloudError> {
213 Ok(self
214 .get_async("https://api.Upcloud.com/1.3/account")
215 .await?
216 .json::<UpcloudAccountRoot>()
217 .await?
218 .account)
219 }
220
221 #[cfg(feature = "blocking")]
222 pub fn get_account_list(&self) -> Result<Vec<UpcloudAccountsListItem>, UpcloudError> {
223 Ok(self
224 .get("https://api.Upcloud.com/1.3/account/list")?
225 .json::<UpcloudAccountsListRoot>()?
226 .accounts
227 .account)
228 }
229
230 pub async fn get_account_list_async(
231 &self,
232 ) -> Result<Vec<UpcloudAccountsListItem>, UpcloudError> {
233 Ok(self
234 .get_async("https://api.Upcloud.com/1.3/account/list")
235 .await?
236 .json::<UpcloudAccountsListRoot>()
237 .await?
238 .accounts
239 .account)
240 }
241
242 #[cfg(feature = "blocking")]
243 pub fn get_prices(&self) -> Result<Vec<UpcloudPricesZone>, UpcloudError> {
244 Ok(self
245 .get("https://api.Upcloud.com/1.3/price")?
246 .json::<UpcloudPricesListRoot>()?
247 .prices
248 .zone)
249 }
250
251 pub async fn get_prices_async(&self) -> Result<Vec<UpcloudPricesZone>, UpcloudError> {
252 Ok(self
253 .get_async("https://api.Upcloud.com/1.3/price")
254 .await?
255 .json::<UpcloudPricesListRoot>()
256 .await?
257 .prices
258 .zone)
259 }
260
261 #[cfg(feature = "blocking")]
262 pub fn get_zones(&self) -> Result<Vec<UpcloudZone>, UpcloudError> {
263 Ok(self
264 .get("https://api.Upcloud.com/1.3/zone")?
265 .json::<UpcloudZoneListRoot>()?
266 .zones
267 .zone)
268 }
269
270 pub async fn get_zones_async(&self) -> Result<Vec<UpcloudZone>, UpcloudError> {
271 Ok(self
272 .get_async("https://api.Upcloud.com/1.3/zone")
273 .await?
274 .json::<UpcloudZoneListRoot>()
275 .await?
276 .zones
277 .zone)
278 }
279
280 #[cfg(feature = "blocking")]
281 pub fn get_plans(&self) -> Result<Vec<UpcloudPlan>, UpcloudError> {
282 Ok(self
283 .get("https://api.Upcloud.com/1.3/plan")?
284 .json::<UpcloudPlanListRoot>()?
285 .plans
286 .plan)
287 }
288
289 pub async fn get_plans_async(&self) -> Result<Vec<UpcloudPlan>, UpcloudError> {
290 Ok(self
291 .get_async("https://api.Upcloud.com/1.3/plan")
292 .await?
293 .json::<UpcloudPlanListRoot>()
294 .await?
295 .plans
296 .plan)
297 }
298
299 #[cfg(feature = "blocking")]
300 pub fn get_servers(&self) -> Result<Vec<UpcloudServer>, UpcloudError> {
301 Ok(self
302 .get("https://api.Upcloud.com/1.3/server")?
303 .json::<UpcloudServerListRoot>()?
304 .servers
305 .server)
306 }
307
308 pub async fn get_servers_async(&self) -> Result<Vec<UpcloudServer>, UpcloudError> {
309 Ok(self
310 .get_async("https://api.Upcloud.com/1.3/server")
311 .await?
312 .json::<UpcloudServerListRoot>()
313 .await?
314 .servers
315 .server)
316 }
317
318 #[cfg(feature = "blocking")]
319 pub fn get_server_details(&self, machine_id: &str) -> Result<UpcloudServer, UpcloudError> {
320 Ok(self
321 .get(&format!("https://api.Upcloud.com/1.3/server/{uuid}", uuid = machine_id))?
322 .json::<UpcloudServerRoot>()?
323 .server)
324 }
325
326 pub async fn get_server_details_async(&self, machine_id: &str) -> Result<UpcloudServer, UpcloudError> {
327 Ok(self
328 .get_async(&format!("https://api.Upcloud.com/1.3/server/{uuid}", uuid = machine_id))
329 .await?
330 .json::<UpcloudServerRoot>()
331 .await?
332 .server)
333 }
334
335 #[cfg(feature = "blocking")]
336 pub fn get_server_templates(&self) -> Result<Vec<UpcloudServerTemplate>, UpcloudError> {
337 Ok(self
338 .get("https://api.Upcloud.com/1.3/storage/template")?
339 .json::<UpcloudServerTemplateListRoot>()?
340 .storages
341 .storage)
342 }
343
344 pub async fn get_server_templates_async(
345 &self,
346 ) -> Result<Vec<UpcloudServerTemplate>, UpcloudError> {
347 Ok(self
348 .get_async("https://api.Upcloud.com/1.3/storage/template")
349 .await?
350 .json::<UpcloudServerTemplateListRoot>()
351 .await?
352 .storages
353 .storage)
354 }
355
356 pub fn create_instance<S1, S2, S3, S4, S5>(
358 &self,
359 region_id: S1,
360 plan_id: S2,
361 os_id: S3,
362 title: S4,
363 hostname: S5,
364 ) -> CreateInstanceBuilder
365 where
366 S1: Into<String> + Serialize,
367 S2: Into<String> + Serialize,
368 S3: Into<String> + Serialize,
369 S4: Into<String> + Serialize,
370 S5: Into<String> + Serialize,
371 {
372 CreateInstanceBuilder::new(self.clone(), region_id, plan_id, os_id, title, hostname)
373 }
374
375 #[cfg(feature = "blocking")]
376 pub fn delete_instance(&self, machine_uuid: &str) -> Result<(), UpcloudError> {
377 self.delete(&format!(
378 "https://api.Upcloud.com/1.3/server/{uuid}?storages=true&backups=delete",
379 uuid = machine_uuid
380 ))?;
381 Ok(())
382 }
383
384 pub async fn delete_instance_async(&self, machine_uuid: &str) -> Result<(), UpcloudError> {
385 self.delete_async(&format!(
386 "https://api.Upcloud.com/1.3/server/{uuid}?storages=true&backups=delete",
387 uuid = machine_uuid
388 ))
389 .await?;
390 Ok(())
391 }
392
393 #[cfg(feature = "blocking")]
394 pub fn stop_instance(&self, machine_uuid: &str) -> Result<UpcloudServer, UpcloudError> {
395 let server = self
396 .post(
397 &format!(
398 "https://api.Upcloud.com/1.3/server/{uuid}/stop",
399 uuid = machine_uuid
400 ),
401 json! ({
402 "stop_server": {
403 "stop_type": "hard"
404 }
405 }),
406 )?
407 .json::<UpcloudServerRoot>()?
408 .server;
409 Ok(server)
410 }
411
412 pub async fn stop_instance_async(
413 &self,
414 machine_uuid: &str,
415 ) -> Result<UpcloudServer, UpcloudError> {
416 let server = self
417 .post_async(
418 &format!(
419 "https://api.Upcloud.com/1.3/server/{uuid}/stop",
420 uuid = machine_uuid
421 ),
422 json! ({
423 "stop_server": {
424 "stop_type": "hard"
425 }
426 }),
427 )
428 .await?
429 .json::<UpcloudServerRoot>()
430 .await?
431 .server;
432 Ok(server)
433 }
434}