1mod api_error;
60mod builder;
61mod data;
62mod linode_error;
63
64use api_error::LinodeApiError;
65use data::linode_availability::LinodeAvailabilityListRoot;
66use data::linode_instance::LinodeInstanceListRoot;
67use data::linode_region::LinodeRegionListRoot;
68use data::PostEmpty;
69use data::{linode_os::LinodeOsListRoot, linode_types::LinodeTypeListRoot};
70use serde::Serialize;
71
72pub use builder::create_instance::LinodeCreateInstanceBuilder;
73pub use data::linode_availability::LinodeAvailability;
74pub use data::linode_instance::{
75 LinodeInstance, LinodeInstanceAlerts, LinodeInstanceBackupSchedule, LinodeInstanceBackups,
76 LinodeInstanceSpecs,
77};
78pub use data::linode_os::LinodeOs;
79pub use data::linode_region::{LinodeRegion, LinodeRegionResolver};
80pub use data::linode_types::{
81 LinodeType, LinodeTypeAddon, LinodeTypeAddons, LinodeTypePrice, LinodeTypeRegionPrice,
82};
83pub use linode_error::LinodeError;
84
85#[derive(Clone)]
86pub struct LinodeApi {
87 token: String,
88}
89
90impl<'a> LinodeApi {
91 pub fn new<S>(token: S) -> LinodeApi
92 where
93 S: Into<String>,
94 {
95 LinodeApi {
96 token: token.into(),
97 }
98 }
99
100 async fn get_async(&self, url: &str) -> Result<reqwest::Response, LinodeError> {
101 let client = reqwest::Client::new();
102 let resp = client
103 .get(url)
104 .bearer_auth(&self.token)
105 .send()
106 .await
107 .map_err(|e| LinodeError::Reqwest(e))?;
108 let status = resp.status();
109 if status.is_client_error() {
110 let result: LinodeApiError = resp.json().await?;
111 Err(LinodeError::Api(result))
112 } else {
113 Ok(resp.error_for_status()?)
114 }
115 }
116
117 #[cfg(feature = "blocking")]
118 fn get(&self, url: &str) -> Result<reqwest::blocking::Response, LinodeError> {
119 let client = reqwest::blocking::Client::new();
120 let resp = client.get(url).bearer_auth(&self.token).send()?;
121 let status = resp.status();
122 if status.is_client_error() {
123 let result: LinodeApiError = resp.json()?;
124 Err(LinodeError::Api(result))
125 } else {
126 Ok(resp.error_for_status()?)
127 }
128 }
129
130 async fn post_async<T>(&self, url: &str, json: T) -> Result<reqwest::Response, LinodeError>
131 where
132 T: Serialize + Sized,
133 {
134 let client = reqwest::Client::new();
135 let resp = client
136 .post(url)
137 .bearer_auth(&self.token)
138 .json(&json)
139 .send()
140 .await?;
141 let status = resp.status();
142 if status.is_client_error() {
143 let result: LinodeApiError = resp.json().await?;
144 Err(LinodeError::Api(result))
145 } else {
146 Ok(resp.error_for_status()?)
147 }
148 }
149
150 #[cfg(feature = "blocking")]
151 fn post<T>(&self, url: &str, json: T) -> Result<reqwest::blocking::Response, LinodeError>
152 where
153 T: Serialize + Sized,
154 {
155 let client = reqwest::blocking::Client::new();
156 let resp = client
157 .post(url)
158 .bearer_auth(&self.token)
159 .json(&json)
160 .send()?;
161 let status = resp.status();
162 if status.is_client_error() {
163 let result: LinodeApiError = resp.json()?;
164 Err(LinodeError::Api(result))
165 } else {
166 Ok(resp.error_for_status()?)
167 }
168 }
169
170 async fn delete_async(&self, url: &str) -> Result<reqwest::Response, LinodeError> {
171 let client = reqwest::Client::new();
172 let resp = client.delete(url).bearer_auth(&self.token).send().await?;
173 let status = resp.status();
174 if status.is_client_error() {
175 let result: LinodeApiError = resp.json().await?;
176 Err(LinodeError::Api(result))
177 } else {
178 Ok(resp.error_for_status()?)
179 }
180 }
181
182 #[cfg(feature = "blocking")]
183 fn delete(&self, url: &str) -> Result<reqwest::blocking::Response, LinodeError> {
184 let client = reqwest::blocking::Client::new();
185 let resp = client.delete(url).bearer_auth(&self.token).send()?;
186 let status = resp.status();
187 if status.is_client_error() {
188 let result: LinodeApiError = resp.json()?;
189 Err(LinodeError::Api(result))
190 } else {
191 Ok(resp.error_for_status()?)
192 }
193 }
194
195 pub async fn list_os_async(&self) -> Result<Vec<LinodeOs>, LinodeError> {
196 let mut list = vec![];
197 let mut page = 1;
198 loop {
199 let result = self
200 .get_async(&format!(
201 "https://api.linode.com/v4/images?page={page}",
202 page = page
203 ))
204 .await?
205 .json::<LinodeOsListRoot>()
206 .await?;
207 if result.data.len() > 0 {
208 list.extend(result.data.into_iter());
209 }
210 page += 1;
211 if page > result.pages {
212 break;
213 }
214 }
215 Ok(list)
216 }
217
218 #[cfg(feature = "blocking")]
219 pub fn list_os(&self) -> Result<Vec<LinodeOs>, LinodeError> {
220 let mut list = vec![];
221 let mut page = 1;
222 loop {
223 let result = self
224 .get(&format!(
225 "https://api.linode.com/v4/images?page={page}",
226 page = page
227 ))?
228 .json::<LinodeOsListRoot>()?;
229 if result.data.len() > 0 {
230 list.extend(result.data.into_iter());
231 }
232 page += 1;
233 if page > result.pages {
234 break;
235 }
236 }
237 Ok(list)
238 }
239
240 pub async fn list_types_async(&self) -> Result<Vec<LinodeType>, LinodeError> {
241 let mut list = vec![];
242 let mut page = 1;
243 loop {
244 let result = self
245 .get_async(&format!(
246 "https://api.linode.com/v4/linode/types?page={page}",
247 page = page
248 ))
249 .await?
250 .json::<LinodeTypeListRoot>()
251 .await?;
252 if result.data.len() > 0 {
253 list.extend(result.data.into_iter());
254 }
255 page += 1;
256 if page > result.pages {
257 break;
258 }
259 }
260 Ok(list)
261 }
262
263 #[cfg(feature = "blocking")]
264 pub fn list_types(&self) -> Result<Vec<LinodeType>, LinodeError> {
265 let mut list = vec![];
266 let mut page = 1;
267 loop {
268 let result = self
269 .get(&format!(
270 "https://api.linode.com/v4/linode/types?page={page}",
271 page = page
272 ))?
273 .json::<LinodeTypeListRoot>()?;
274 if result.data.len() > 0 {
275 list.extend(result.data.into_iter());
276 }
277 page += 1;
278 if page > result.pages {
279 break;
280 }
281 }
282 Ok(list)
283 }
284
285 pub async fn list_regions_async(&self) -> Result<Vec<LinodeRegion>, LinodeError> {
286 let mut list = vec![];
287 let mut page = 1;
288 loop {
289 let result = self
290 .get_async(&format!(
291 "https://api.linode.com/v4/regions?page={page}",
292 page = page
293 ))
294 .await?
295 .json::<LinodeRegionListRoot>()
296 .await?;
297 if result.data.len() > 0 {
298 list.extend(result.data.into_iter());
299 }
300 page += 1;
301 if page > result.pages {
302 break;
303 }
304 }
305 Ok(list)
306 }
307
308 #[cfg(feature = "blocking")]
309 pub fn list_regions(&self) -> Result<Vec<LinodeRegion>, LinodeError> {
310 let mut list = vec![];
311 let mut page = 1;
312 loop {
313 let result = self
314 .get(&format!(
315 "https://api.linode.com/v4/regions?page={page}",
316 page = page
317 ))?
318 .json::<LinodeRegionListRoot>()?;
319 if result.data.len() > 0 {
320 list.extend(result.data.into_iter());
321 }
322 page += 1;
323 if page > result.pages {
324 break;
325 }
326 }
327 Ok(list)
328 }
329
330 pub async fn list_availability_async(&self) -> Result<Vec<LinodeAvailability>, LinodeError> {
331 let mut list = vec![];
332 let mut page = 1;
333 loop {
334 let result = self
335 .get_async(&format!(
336 "https://api.linode.com/v4/regions/availability?page={page}",
337 page = page
338 ))
339 .await?
340 .json::<LinodeAvailabilityListRoot>()
341 .await?;
342 if result.data.len() > 0 {
343 list.extend(result.data.into_iter());
344 }
345 page += 1;
346 if page > result.pages {
347 break;
348 }
349 }
350 Ok(list)
351 }
352
353 #[cfg(feature = "blocking")]
354 pub fn list_availability(&self) -> Result<Vec<LinodeAvailability>, LinodeError> {
355 let mut list = vec![];
356 let mut page = 1;
357 loop {
358 let result = self
359 .get(&format!(
360 "https://api.linode.com/v4/regions/availability?page={page}",
361 page = page
362 ))?
363 .json::<LinodeAvailabilityListRoot>()?;
364 if result.data.len() > 0 {
365 list.extend(result.data.into_iter());
366 }
367 page += 1;
368 if page > result.pages {
369 break;
370 }
371 }
372 Ok(list)
373 }
374
375 pub async fn list_instances_async(&self) -> Result<Vec<LinodeInstance>, LinodeError> {
376 let mut list = vec![];
377 let mut page = 1;
378 loop {
379 let result = self
380 .get_async(&format!(
381 "https://api.linode.com/v4/linode/instances?page={page}",
382 page = page
383 ))
384 .await?
385 .json::<LinodeInstanceListRoot>()
386 .await?;
387 if result.data.len() > 0 {
388 list.extend(result.data.into_iter());
389 }
390 page += 1;
391 if page > result.pages {
392 break;
393 }
394 }
395 Ok(list)
396 }
397
398 #[cfg(feature = "blocking")]
399 pub fn list_instances(&self) -> Result<Vec<LinodeInstance>, LinodeError> {
400 let mut list = vec![];
401 let mut page = 1;
402 loop {
403 let result = self
404 .get(&format!(
405 "https://api.linode.com/v4/linode/instances?page={page}",
406 page = page
407 ))?
408 .json::<LinodeInstanceListRoot>()?;
409 if result.data.len() > 0 {
410 list.extend(result.data.into_iter());
411 }
412 page += 1;
413 if page > result.pages {
414 break;
415 }
416 }
417 Ok(list)
418 }
419
420 pub async fn get_instance_async(
421 &self,
422 instance_id: u64,
423 ) -> Result<LinodeInstance, LinodeError> {
424 let instance = self
425 .get_async(&format!(
426 "https://api.linode.com/v4/linode/instances/{instance_id}",
427 instance_id = instance_id,
428 ))
429 .await?
430 .json::<LinodeInstance>()
431 .await?;
432 Ok(instance)
433 }
434
435 #[cfg(feature = "blocking")]
436 pub fn get_instance(&self, instance_id: u64) -> Result<LinodeInstance, LinodeError> {
437 let instance = self
438 .get(&format!(
439 "https://api.linode.com/v4/linode/instances/{instance_id}",
440 instance_id = instance_id,
441 ))?
442 .json::<LinodeInstance>()?;
443 Ok(instance)
444 }
445
446 pub async fn delete_instance_async(&self, instance_id: u64) -> Result<(), LinodeError> {
447 self.delete_async(&format!(
448 "https://api.linode.com/v4/linode/instances/{instance_id}",
449 instance_id = instance_id,
450 ))
451 .await?
452 .error_for_status()?;
453 Ok(())
454 }
455
456 #[cfg(feature = "blocking")]
457 pub fn delete_instance(&self, instance_id: u64) -> Result<(), LinodeError> {
458 self.delete(&format!(
459 "https://api.linode.com/v4/linode/instances/{instance_id}",
460 instance_id = instance_id,
461 ))?
462 .error_for_status()?;
463 Ok(())
464 }
465
466 pub async fn shutdown_instance_async(&self, instance_id: u64) -> Result<(), LinodeError> {
467 self.post_async(
468 &format!(
469 "https://api.linode.com/v4/linode/instances/{instance_id}/shutdown",
470 instance_id = instance_id,
471 ),
472 PostEmpty {},
473 )
474 .await?
475 .error_for_status()?;
476 Ok(())
477 }
478
479 #[cfg(feature = "blocking")]
480 pub fn shutdown_instance(&self, instance_id: u64) -> Result<(), LinodeError> {
481 self.post(
482 &format!(
483 "https://api.linode.com/v4/linode/instances/{instance_id}/shutdown",
484 instance_id = instance_id,
485 ),
486 PostEmpty {},
487 )?
488 .error_for_status()?;
489 Ok(())
490 }
491
492 pub fn create_instance(&self, region: &str, ltype: &str) -> LinodeCreateInstanceBuilder {
493 LinodeCreateInstanceBuilder::new(self.clone(), region, ltype)
494 }
495}