mod api_error;
mod builder;
mod data;
mod digitalocean_error;
use builder::create_droplet_builder::CreateDropletBuilder;
use data::{
droplet::{DigitalOceanDropletRoot, DigitalOceanDropletsRoot},
droplet_action::{DigitalOceanDropletAction, DigitalOceanDropletActionRoot},
image::DigitalOceanImagesRoot,
size::DigitalOceanSizesRoot,
};
use serde::Serialize;
pub use api_error::DigitalOceanApiError;
pub use data::{
droplet::{
DigitalOceanBackupWindow, DigitalOceanDroplet, DigitalOceanNetwork, DigitalOceanNetworks,
DigitalOceanRegion,
},
image::DigitalOceanImage,
size::DigitalOceanSize,
};
pub use digitalocean_error::DigitalOceanError;
use serde_json::json;
#[derive(Clone)]
pub struct DigitalOceanApi {
api_key: String,
}
impl<'a> DigitalOceanApi {
pub fn new<S>(api_key: S) -> DigitalOceanApi
where
S: Into<String>,
{
DigitalOceanApi {
api_key: api_key.into(),
}
}
async fn get_async(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::Response, DigitalOceanError> {
let client = reqwest::Client::new();
let resp = 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(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::blocking::Response, DigitalOceanError> {
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 client = reqwest::Client::new();
let resp = 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 client = reqwest::Client::new();
let resp = 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 async fn list_images_async(&self) -> Result<Vec<DigitalOceanImage>, DigitalOceanError> {
let images = self
.get_async("https://api.digitalocean.com/v2/images", vec![])
.await?
.json::<DigitalOceanImagesRoot>()
.await?
.images;
Ok(images)
}
#[cfg(feature = "blocking")]
pub fn list_images(&self) -> Result<Vec<DigitalOceanImage>, DigitalOceanError> {
let images = self
.get("https://api.digitalocean.com/v2/images", vec![])?
.json::<DigitalOceanImagesRoot>()?
.images;
Ok(images)
}
pub async fn list_sizes_async(&self) -> Result<Vec<DigitalOceanSize>, DigitalOceanError> {
let sizes = self
.get_async("https://api.digitalocean.com/v2/sizes", vec![])
.await?
.json::<DigitalOceanSizesRoot>()
.await?
.sizes;
Ok(sizes)
}
#[cfg(feature = "blocking")]
pub fn list_sizes(&self) -> Result<Vec<DigitalOceanSize>, DigitalOceanError> {
let sizes = self
.get("https://api.digitalocean.com/v2/sizes", vec![])?
.json::<DigitalOceanSizesRoot>()?
.sizes;
Ok(sizes)
}
pub async fn get_droplet_async(
&self,
droplet_id: &str,
) -> Result<DigitalOceanDroplet, DigitalOceanError> {
let droplet = self
.get_async(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
),
vec![],
)
.await?
.json::<DigitalOceanDropletRoot>()
.await?
.droplet;
Ok(droplet)
}
#[cfg(feature = "blocking")]
pub fn get_droplet(&self, droplet_id: &str) -> Result<DigitalOceanDroplet, DigitalOceanError> {
let droplet = self
.get(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}",
droplet_id = droplet_id
),
vec![],
)?
.json::<DigitalOceanDropletRoot>()?
.droplet;
Ok(droplet)
}
pub async fn list_droplets_async(&self) -> Result<Vec<DigitalOceanDroplet>, DigitalOceanError> {
let droplets = self
.get_async("https://api.digitalocean.com/v2/droplets", vec![])
.await?
.json::<DigitalOceanDropletsRoot>()
.await?
.droplets;
Ok(droplets)
}
#[cfg(feature = "blocking")]
pub fn list_droplets(&self) -> Result<Vec<DigitalOceanDroplet>, DigitalOceanError> {
let droplets = self
.get("https://api.digitalocean.com/v2/droplets", vec![])?
.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 action = self
.get_async(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}",
droplet_id = droplet_id,
action_id = action_id,
),
vec![],
)
.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 action = self
.get(
&format!(
"https://api.digitalocean.com/v2/droplets/{droplet_id}/actions/{action_id}",
droplet_id = droplet_id,
action_id = action_id,
),
vec![],
)?
.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(())
}
}