Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle error with thiserror #4

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
phf = { version = "0.10", features = ["macros"] }
configparser = "2.1.0"
thiserror = "1.0"

[dev-dependencies]
clap = "3.0.0-beta.4"
86 changes: 57 additions & 29 deletions src/client.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Low-level access to the OVH API.

use crate::error::OvhError;
use configparser::ini::Ini;
use reqwest::{header::HeaderMap, Response};
use serde::Serialize;
Expand Down Expand Up @@ -107,33 +108,37 @@ impl OvhClient {
/// ; with a single consumer key.
/// ;consumer_key=my_consumer_key
/// ```
pub fn from_conf<T>(path: T) -> Result<Self, Box<dyn std::error::Error>>
pub fn from_conf<T>(path: T) -> Result<Self, OvhError>
where
T: AsRef<Path>,
{
let mut conf = Ini::new();
conf.load(path)?;
conf.load(path).map_err(OvhError::Generic)?;

let endpoint = conf
.get("default", "endpoint")
.ok_or("missing key `endpoint`")?;
.ok_or(OvhError::Generic("missing key `endpoint`".to_owned()))?;
let application_key = conf
.get(&endpoint, "application_key")
.ok_or("missing key `application_key`")?;
let application_secret = conf
.get(&endpoint, "application_secret")
.ok_or("missing key `application_secret`")?;
.ok_or(OvhError::Generic(
"missing key `application_key`".to_owned(),
))?;
let application_secret =
conf.get(&endpoint, "application_secret")
.ok_or(OvhError::Generic(
"missing key `application_secret`".to_owned(),
))?;
let consumer_key = conf
.get(&endpoint, "consumer_key")
.ok_or("missing key `consumer_key`")?;
.ok_or(OvhError::Generic("missing key `consumer_key`".to_owned()))?;

let c = Self::new(
&endpoint,
&application_key,
&application_secret,
&consumer_key,
)
.ok_or("failed to create client")?;
.ok_or(OvhError::Generic("failed to create client".to_owned()))?;

Ok(c)
}
Expand All @@ -160,9 +165,19 @@ impl OvhClient {
/// This method will perform a request to the API server to get its
/// local time, and then subtract it from the local time of the machine.
/// The result is a time delta value, is seconds.
pub async fn time_delta(&self) -> Result<i64, Box<dyn std::error::Error>> {
let server_time: u64 = self.get_noauth("/auth/time").await?.text().await?.parse()?;
let delta = (now() - server_time).try_into()?;
pub async fn time_delta(&self) -> Result<i64, OvhError> {
let server_time: u64 = self
.get_noauth("/auth/time")
.await?
.text()
.await
.map_err(OvhError::Reqwest)?
.parse()
.map_err(OvhError::ParseIntError)?;

let delta = (now() - server_time)
.try_into()
.map_err(OvhError::TryFromInt)?;
Ok(delta)
}

Expand All @@ -180,11 +195,11 @@ impl OvhClient {
url: &str,
method: &str,
body: &str,
) -> Result<HeaderMap, Box<dyn std::error::Error>> {
) -> Result<HeaderMap, OvhError> {
let mut headers = self.default_headers();

let time_delta = self.time_delta().await?;
let now: i64 = now().try_into()?;
let now: i64 = now().try_into().map_err(OvhError::TryFromInt)?;
let timestamp = now + time_delta;
let timestamp = timestamp.to_string();

Expand All @@ -198,23 +213,32 @@ impl OvhClient {
}

/// Performs a GET request.
pub async fn get(&self, path: &str) -> Result<reqwest::Response, Box<dyn std::error::Error>> {
pub async fn get(&self, path: &str) -> Result<reqwest::Response, OvhError> {
let url = self.url(path);
let headers = self.gen_headers(&url, "GET", "").await?;

let resp = self.client.get(url).headers(headers).send().await?;
let resp = self
.client
.get(url)
.headers(headers)
.send()
.await
.map_err(OvhError::Reqwest)?;
Ok(resp)
}

/// Performs a DELETE request.
pub async fn delete(
&self,
path: &str,
) -> Result<reqwest::Response, Box<dyn std::error::Error>> {
pub async fn delete(&self, path: &str) -> Result<reqwest::Response, OvhError> {
let url = self.url(path);
let headers = self.gen_headers(&url, "DELETE", "").await?;

let resp = self.client.delete(url).headers(headers).send().await?;
let resp = self
.client
.delete(url)
.headers(headers)
.send()
.await
.map_err(OvhError::Reqwest)?;
Ok(resp)
}

Expand All @@ -223,12 +247,12 @@ impl OvhClient {
&self,
path: &str,
data: &T,
) -> Result<Response, Box<dyn std::error::Error>> {
) -> Result<Response, OvhError> {
let url = self.url(path);

// Cannot call RequestBuilder.json directly because of body
// signature requirement.
let body = serde_json::to_string(data)?;
let body = serde_json::to_string(data).map_err(OvhError::Serde)?;
let headers = self.gen_headers(&url, "POST", &body).await?;

let resp = self
Expand All @@ -237,19 +261,23 @@ impl OvhClient {
.headers(headers)
.body(body)
.send()
.await?;
.await
.map_err(OvhError::Reqwest)?;
Ok(resp)
}

/// Performs a GET request without auth.
pub async fn get_noauth(
&self,
path: &str,
) -> Result<reqwest::Response, Box<dyn std::error::Error>> {
pub async fn get_noauth(&self, path: &str) -> Result<reqwest::Response, OvhError> {
let url = self.url(path);
let headers = self.default_headers();

let resp = self.client.get(url).headers(headers).send().await?;
let resp = self
.client
.get(url)
.headers(headers)
.send()
.await
.map_err(OvhError::Reqwest)?;
Ok(resp)
}
}
26 changes: 12 additions & 14 deletions src/email_redir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::fmt::Display;
use crate::client::OvhClient;
use reqwest::Response;

use crate::error::OvhError;
use serde::{Deserialize, Serialize};

/// Structure representing a single email redirection.
Expand All @@ -25,12 +26,13 @@ impl OvhMailRedir {
client: &OvhClient,
domain: &str,
id: &str,
) -> Result<OvhMailRedir, Box<dyn std::error::Error>> {
) -> Result<OvhMailRedir, OvhError> {
let res = client
.get(&format!("/email/domain/{}/redirection/{}", domain, id))
.await?
.json()
.await?;
.await
.map_err(OvhError::Reqwest)?;
Ok(res)
}

Expand All @@ -55,16 +57,16 @@ impl OvhMailRedir {
/// }
/// }
/// ```
pub async fn list(
client: &OvhClient,
domain: &str,
) -> Result<Vec<OvhMailRedir>, Box<dyn std::error::Error>> {
pub async fn list(client: &OvhClient, domain: &str) -> Result<Vec<OvhMailRedir>, OvhError> {
let resp = client
.get(&format!("/email/domain/{}/redirection", domain))
.await?;
let resp = resp.error_for_status()?;
let resp = resp.error_for_status().map_err(OvhError::Reqwest)?;

let res = resp.json::<Vec<String>>().await?;
let res = resp
.json::<Vec<String>>()
.await
.map_err(OvhError::Reqwest)?;
let res: Vec<_> =
futures::future::join_all(res.iter().map(|id| Self::get_redir(client, domain, id)))
.await;
Expand Down Expand Up @@ -94,7 +96,7 @@ impl OvhMailRedir {
from: &str,
to: &str,
local_copy: bool,
) -> Result<Response, Box<dyn std::error::Error>> {
) -> Result<Response, OvhError> {
let data = OvhMailRedirCreate {
from,
to,
Expand All @@ -118,11 +120,7 @@ impl OvhMailRedir {
/// .unwrap();
/// }
/// ```
pub async fn delete(
c: &OvhClient,
domain: &str,
id: &str,
) -> Result<Response, Box<dyn std::error::Error>> {
pub async fn delete(c: &OvhClient, domain: &str, id: &str) -> Result<Response, OvhError> {
c.delete(&format!("/email/domain/{}/redirection/{}", domain, id))
.await
}
Expand Down
22 changes: 22 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
use reqwest::Error as ReqError;
use serde_json::Error as SerError;
use std::num::{ParseIntError, TryFromIntError};
use thiserror::Error;

#[derive(Error, Debug)]
pub enum OvhError {
#[error("network issue")]
Reqwest(#[from] ReqError),

#[error("parseInt issue")]
ParseIntError(#[from] ParseIntError),

#[error("tryFromInt issue")]
TryFromInt(#[from] TryFromIntError),

#[error("serde issue")]
Serde(#[from] SerError),

#[error("generic error : `{0}`")]
Generic(String),
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

pub mod client;
pub mod email_redir;
pub mod error;