mod api_error;
mod builder;
mod data;
mod digitalocean_error;
use builder::{
create_droplet_builder::CreateDropletBuilder, list_images_builder::ListImagesBuilder,
};
use data::{
droplet::{DigitalOceanDropletRoot, DigitalOceanDropletsRoot, DigitalOceanRegionsRoot},
droplet_action::{DigitalOceanDropletAction, DigitalOceanDropletActionRoot},
size::DigitalOceanSizesRoot,
};
use reqwest::Client;
use serde::Serialize;
pub use api_error::DigitalOceanApiError;
pub use builder::list_images_builder::ImageType;
pub use data::{
droplet::{
DigitalOceanBackupWindow, DigitalOceanDroplet, DigitalOceanNetworks, DigitalOceanNetworkv4,
DigitalOceanNetworkv6, DigitalOceanRegion,
},
image::DigitalOceanImage,
size::DigitalOceanSize,
};
pub use digitalocean_error::DigitalOceanError;
#[derive(Clone)]
pub struct DigitalOceanApi {
api_key: String,
client: Client,
}
impl<'a> DigitalOceanApi {
pub fn new<S>(api_key: S) -> DigitalOceanApi
where
S: Into<String>,
{
DigitalOceanApi {
api_key: api_key.into(),
client: reqwest::Client::new(),
}
}
async fn get_async<T>(
&self,
url: &str,
query: T,
) -> Result<reqwest::Response, DigitalOceanError>
where
T: Serialize + Sized,
{
let resp = self
.client
.get(url)
.bearer_auth(&self.api_key)
.query(&query)
.send()
.await
.map_err(|e| DigitalOceanError::Reqwest(e))?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json().await?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn get<T>(&self, url: &str, query: T) -> Result<reqwest::blocking::Response, DigitalOceanError>
where
T: Serialize + Sized,
{
let client = reqwest::blocking::Client::new();
let resp = client
.get(url)
.bearer_auth(&self.api_key)
.query(&query)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json()?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
async fn post_async<T>(
&self,
url: &str,
json: T,
) -> Result<reqwest::Response, DigitalOceanError>
where
T: Serialize + Sized,
{
let resp = self.client
.post(url)
.bearer_auth(&self.api_key)
.json(&json)
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json().await?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn post<T>(&self, url: &str, json: T) -> Result<reqwest::blocking::Response, DigitalOceanError>
where
T: Serialize + Sized,
{
let client = reqwest::blocking::Client::new();
let resp = client
.post(url)
.bearer_auth(&self.api_key)
.json(&json)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json()?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
async fn delete_async(&self, url: &str) -> Result<reqwest::Response, DigitalOceanError> {
let resp = self.client.delete(url).bearer_auth(&self.api_key).send().await?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json().await?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn delete(&self, url: &str) -> Result<reqwest::blocking::Response, DigitalOceanError> {
let client = reqwest::blocking::Client::new();
let resp = client.delete(url).bearer_auth(&self.api_key).send()?;
let status = resp.status();
if status.is_client_error() {
let result: DigitalOceanApiError = resp.json()?;
Err(DigitalOceanError::Api(result))
} else {
Ok(resp.error_for_status()?)
}
}
pub fn list_images(&self) -> ListImagesBuilder {
ListImagesBuilder::new(self.clone())
}
pub async fn list_sizes_async(&self) -> Result<Vec<DigitalOceanSize>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let sizes = self
.get_async("https://api.digitalocean.com/v2/sizes", query)
.await?
.json::<DigitalOceanSizesRoot>()
.await?
.sizes;
Ok(sizes)
}
#[cfg(feature = "blocking")]
pub fn list_sizes(&self) -> Result<Vec<DigitalOceanSize>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let sizes = self
.get("https://api.digitalocean.com/v2/sizes", query)?
.json::<DigitalOceanSizesRoot>()?
.sizes;
Ok(sizes)
}
pub async fn list_regions_async(&self) -> Result<Vec<DigitalOceanRegion>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let regions = self
.get_async("https://api.digitalocean.com/v2/regions", query)
.await?
.json::<DigitalOceanRegionsRoot>()
.await?
.regions;
Ok(regions)
}
#[cfg(feature = "blocking")]
pub fn list_regions(&self) -> Result<Vec<DigitalOceanRegion>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let regions = self
.get("https://api.digitalocean.com/v2/regions", query)?
.json::<DigitalOceanRegionsRoot>()?
.regions;
Ok(regions)
}
pub async fn get_droplet_async(
&self,
droplet_id: &str,
) -> Result<DigitalOceanDroplet, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let droplet = self
.get_async(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
),
query,
)
.await?
.json::<DigitalOceanDropletRoot>()
.await?
.droplet;
Ok(droplet)
}
#[cfg(feature = "blocking")]
pub fn get_droplet(&self, droplet_id: &str) -> Result<DigitalOceanDroplet, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let droplet = self
.get(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
),
query,
)?
.json::<DigitalOceanDropletRoot>()?
.droplet;
Ok(droplet)
}
pub async fn list_droplets_async(&self) -> Result<Vec<DigitalOceanDroplet>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let droplets = self
.get_async("https://api.digitalocean.com/v2/droplets", query)
.await?
.json::<DigitalOceanDropletsRoot>()
.await?
.droplets;
Ok(droplets)
}
#[cfg(feature = "blocking")]
pub fn list_droplets(&self) -> Result<Vec<DigitalOceanDroplet>, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let droplets = self
.get("https://api.digitalocean.com/v2/droplets", query)?
.json::<DigitalOceanDropletsRoot>()?
.droplets;
Ok(droplets)
}
pub fn create_droplet<S1, S2, S3>(
&self,
name: S1,
size_id: S2,
image_id: S3,
) -> CreateDropletBuilder
where
S1: Into<String>,
S2: Into<String>,
S3: Into<String>,
{
CreateDropletBuilder::new(self.clone(), name, size_id, image_id)
}
pub async fn initiate_droplet_action_async<T>(
&self,
droplet_id: &str,
action: T,
) -> Result<DigitalOceanDropletAction, DigitalOceanError>
where
T: Serialize + Sized,
{
let action = self
.post_async(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions",
droplet_id = droplet_id,
),
action,
)
.await?
.json::<DigitalOceanDropletActionRoot>()
.await?
.action;
Ok(action)
}
#[cfg(feature = "blocking")]
pub fn initiate_droplet_action<T>(
&self,
droplet_id: &str,
action: T,
) -> Result<DigitalOceanDropletAction, DigitalOceanError>
where
T: Serialize + Sized,
{
let action = self
.post(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions",
droplet_id = droplet_id,
),
action,
)?
.json::<DigitalOceanDropletActionRoot>()?
.action;
Ok(action)
}
pub async fn get_droplet_action_async(
&self,
droplet_id: &str,
action_id: &str,
) -> Result<DigitalOceanDropletAction, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let action = self
.get_async(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}",
droplet_id = droplet_id,
action_id = action_id,
),
query,
)
.await?
.json::<DigitalOceanDropletActionRoot>()
.await?
.action;
Ok(action)
}
#[cfg(feature = "blocking")]
pub fn get_droplet_action(
&self,
droplet_id: &str,
action_id: &str,
) -> Result<DigitalOceanDropletAction, DigitalOceanError> {
let query: Vec<(String, String)> = Vec::default();
let action = self
.get(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}",
droplet_id = droplet_id,
action_id = action_id,
),
query,
)?
.json::<DigitalOceanDropletActionRoot>()?
.action;
Ok(action)
}
pub async fn delete_droplet_async(&self, droplet_id: &str) -> Result<(), DigitalOceanError> {
self.delete_async(&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
))
.await?
.error_for_status()?;
Ok(())
}
#[cfg(feature = "blocking")]
pub fn delete_droplet(&self, droplet_id: &str) -> Result<(), DigitalOceanError> {
self.delete(&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
))?
.error_for_status()?;
Ok(())
}
}