mod api_error;
mod builder;
mod data;
mod scaleway_error;
use api_error::ScalewayApiError;
use builder::list_instance_builder::ScalewayListInstanceBuilder;
use data::{image::ScalewayImage, image::ScalewayImageRoot, server_type::ScalewayServerTypeRoot};
use serde::Serialize;
pub use data::server_type::ServerType;
pub use scaleway_error::ScalewayError;
#[derive(Clone)]
pub struct ScalewayApi {
secret_key: String,
}
impl<'a> ScalewayApi {
pub fn new<S>(secret_key: S) -> ScalewayApi
where
S: Into<String>,
{
ScalewayApi {
secret_key: secret_key.into(),
}
}
async fn get_async(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::Response, ScalewayError> {
let client = reqwest::Client::new();
let resp = client
.get(url)
.header("X-Auth-Token", &self.secret_key)
.query(&query)
.send()
.await
.map_err(|e| ScalewayError::Reqwest(e))?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn get(
&self,
url: &str,
query: Vec<(&'static str, String)>,
) -> Result<reqwest::blocking::Response, ScalewayError> {
let client = reqwest::blocking::Client::new();
let resp = client
.get(url)
.header("X-Auth-Token", &self.secret_key)
.query(&query)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
async fn post_async<T>(&self, url: &str, json: T) -> Result<reqwest::Response, ScalewayError>
where
T: Serialize + Sized,
{
let client = reqwest::Client::new();
let resp = client
.post(url)
.header("X-Auth-Token", &self.secret_key)
.json(&json)
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn post<T>(&self, url: &str, json: T) -> Result<reqwest::blocking::Response, ScalewayError>
where
T: Serialize + Sized,
{
let client = reqwest::blocking::Client::new();
let resp = client
.post(url)
.header("X-Auth-Token", &self.secret_key)
.json(&json)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
async fn delete_async(&self, url: &str) -> Result<reqwest::Response, ScalewayError> {
let client = reqwest::Client::new();
let resp = client
.delete(url)
.header("X-Auth-Token", &self.secret_key)
.send()
.await?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json().await?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
#[cfg(feature = "blocking")]
fn delete(&self, url: &str) -> Result<reqwest::blocking::Response, ScalewayError> {
let client = reqwest::blocking::Client::new();
let resp = client
.delete(url)
.header("X-Auth-Token", &self.secret_key)
.send()?;
let status = resp.status();
if status.is_client_error() {
let result: ScalewayApiError = resp.json()?;
Err(ScalewayError::Api(result.error))
} else {
Ok(resp.error_for_status()?)
}
}
pub fn az_list() -> Vec<&'static str> {
vec![
"fr-par-1", "fr-par-2", "fr-par-3", "nl-ams-1", "nl-ams-2", "pl-waw-1", "pl-waw-2",
]
}
#[cfg(feature = "blocking")]
pub fn get_server_types(&self) -> Result<Vec<ServerType>, ScalewayError> {
let mut result = vec![];
for zone in ScalewayApi::az_list() {
let types: Vec<ServerType> = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers",
zone = zone
),
vec![],
)?
.json::<ScalewayServerTypeRoot>()?
.servers
.servers
.into_iter()
.map(|(id, item)| ServerType {
id,
location: zone.to_string(),
alt_names: item.alt_names,
arch: item.arch,
ncpus: item.ncpus,
ram: item.ram,
gpu: item.gpu,
baremetal: item.baremetal,
monthly_price: item.monthly_price,
hourly_price: item.hourly_price,
network: item.network,
})
.collect();
result.extend(types.into_iter());
}
Ok(result)
}
pub async fn get_server_types_async(&self) -> Result<Vec<ServerType>, ScalewayError> {
let mut result = vec![];
for zone in ScalewayApi::az_list() {
let types: Vec<ServerType> = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/products/servers",
zone = zone
),
vec![],
)
.await?
.json::<ScalewayServerTypeRoot>()
.await?
.servers
.servers
.into_iter()
.map(|(id, item)| ServerType {
id,
location: zone.to_string(),
alt_names: item.alt_names,
arch: item.arch,
ncpus: item.ncpus,
ram: item.ram,
gpu: item.gpu,
baremetal: item.baremetal,
monthly_price: item.monthly_price,
hourly_price: item.hourly_price,
network: item.network,
})
.collect();
result.extend(types.into_iter());
}
Ok(result)
}
#[cfg(feature = "blocking")]
pub fn get_images(&self) -> Result<Vec<ScalewayImage>, ScalewayError> {
let mut result = vec![];
for zone in ScalewayApi::az_list() {
let types: Vec<ScalewayImage> = self
.get(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/images",
zone = zone
),
vec![],
)?
.json::<ScalewayImageRoot>()?
.images;
result.extend(types.into_iter());
}
Ok(result)
}
pub async fn get_images_async(&self) -> Result<Vec<ScalewayImage>, ScalewayError> {
let mut result = vec![];
for zone in ScalewayApi::az_list() {
let types: Vec<ScalewayImage> = self
.get_async(
&format!(
"https://api.scaleway.com/instance/v1/zones/{zone}/images",
zone = zone
),
vec![],
)
.await?
.json::<ScalewayImageRoot>()
.await?
.images;
result.extend(types.into_iter());
}
Ok(result)
}
pub fn get_instances(&self, zone: &str) -> ScalewayListInstanceBuilder {
ScalewayListInstanceBuilder::new(self.clone(), zone)
}
}