use crate::error::Error;
use humantime::format_duration;
use reqwest::{self, IntoUrl, Response, StatusCode, Url};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, time::Duration};
pub async fn error_for_status(response: Response) -> Result<Response, Error> {
match response.status() {
StatusCode::BAD_REQUEST => {
let message = response.text().await?;
Err(Error::DomainError(message))
}
StatusCode::CONFLICT => {
let message = response.text().await?;
Err(Error::DomainError(message))
}
_ => Ok(response.error_for_status()?),
}
}
#[derive(Debug, Clone)]
pub struct Client {
client: reqwest::Client,
url: Url,
}
impl Client {
pub fn new(url: impl IntoUrl) -> Result<Self, Error> {
let new_instance = Self {
client: reqwest::Client::new(),
url: url.into_url()?,
};
Ok(new_instance)
}
pub async fn new_peer(&self, expires_in: Duration) -> Result<u64, Error> {
let response = self
.client
.post(self.url.join("new_peer").unwrap())
.json(&ExpiresIn { expires_in })
.send()
.await?;
let peer_id = error_for_status(response).await?.json().await?;
Ok(peer_id)
}
pub async fn release(&self, peer_id: u64) -> Result<(), Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", &peer_id.to_string()]);
let response = self.client.delete(url).send().await?;
error_for_status(response).await?;
Ok(())
}
pub async fn acquire(
&self,
peer_id: u64,
semaphore: &str,
count: u32,
expires_in: Option<Duration>,
block_for: Option<Duration>,
) -> Result<bool, Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", &peer_id.to_string(), semaphore]);
{
let mut query = url.query_pairs_mut();
query.extend_pairs(expires_in.map(|d| ("expires_in", format_duration(d).to_string())));
query.extend_pairs(block_for.map(|d| ("block_for", format_duration(d).to_string())));
}
let response = self.client.put(url).json(&count).send().await?;
let response = error_for_status(response).await?;
match response.status() {
StatusCode::OK => Ok(true),
StatusCode::ACCEPTED => Ok(false),
_ => Err(Error::UnexpectedResponse),
}
}
pub async fn is_acquired(&self, peer_id: u64) -> Result<bool, Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", &peer_id.to_string(), "is_acquired"]);
let response = self.client.get(url).send().await?;
let acquired = error_for_status(response).await?.json().await?;
Ok(acquired)
}
pub async fn remainder(&self, semaphore: &str) -> Result<i64, Error> {
let mut url = self.url.join("remainder").unwrap();
url.query_pairs_mut().append_pair("semaphore", semaphore);
let response = self.client.get(url).send().await?;
let remainder = error_for_status(response).await?.json().await?;
Ok(remainder)
}
pub async fn restore(
&self,
peer_id: u64,
acquired: &HashMap<String, u32>,
expires_in: Duration,
) -> Result<(), Error> {
let url = self.url.join("restore").unwrap();
let response = self
.client
.post(url)
.json(&Restore {
expires_in,
peer_id,
acquired,
})
.send()
.await?;
error_for_status(response).await?;
Ok(())
}
pub async fn heartbeat(&self, peer_id: u64, expires_in: Duration) -> Result<(), Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", &peer_id.to_string()]);
let response = self
.client
.put(url)
.json(&ExpiresIn { expires_in })
.send()
.await?;
error_for_status(response).await?;
Ok(())
}
pub async fn release_lock(&self, peer_id: u64, semaphore: &str) -> Result<(), Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", &peer_id.to_string(), semaphore]);
let response = self.client.delete(url).send().await?;
error_for_status(response).await?;
Ok(())
}
pub async fn list_of_peers(&self) -> Result<Vec<PeerDescription>, Error> {
let mut url = self.url.clone();
url.path_segments_mut()
.unwrap()
.extend(["peers", ""]);
let response = self.client.get(url).send().await?;
let peers = error_for_status(response).await?.json().await?;
Ok(peers)
}
}
#[derive(Deserialize)]
pub struct PeerDescription {}
#[derive(Serialize)]
struct ExpiresIn {
#[serde(with = "humantime_serde")]
expires_in: Duration,
}
#[derive(Serialize)]
struct Restore<'a> {
#[serde(with = "humantime_serde")]
expires_in: Duration,
peer_id: u64,
acquired: &'a HashMap<String, u32>,
}