use crate::config::RequestConfig;
use crate::jobs;
use crate::request_error::Error;
use serde::de::DeserializeOwned;
use serde::Serialize;
use std::collections::HashMap;
const HEADER_RESPONSE_STATUS: &str = "x-response-status";
const HEADER_RESPONSE_MESSAGE: &str = "x-response-message";
const STATUS_SUCCESS: u8 = 1;
const STATUS_ERROR: u8 = 0;
#[derive(Default)]
pub struct Request<T>
where
T: Serialize,
{
config: RequestConfig,
endpoint: String,
method: reqwest::Method,
auth: Option<Authorization>,
payload: Option<T>,
}
#[derive(Debug)]
pub enum AuthorizationType {
Bearer,
Basic,
}
impl std::fmt::Display for AuthorizationType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
pub struct Authorization {
auth_type: AuthorizationType,
payload: String,
}
impl Authorization {
pub fn new(auth_type: AuthorizationType, payload: String) -> Self {
Authorization { auth_type, payload }
}
}
impl<T> Request<T>
where
T: Serialize + Default,
{
pub fn new(config: RequestConfig, endpoint: &str, p: T) -> Self {
Request {
config,
endpoint: endpoint.to_owned(),
payload: Some(p),
..Request::default()
}
}
pub fn with_auth(&mut self, auth: Authorization) {
self.auth = Some(auth);
}
pub fn with_method(&mut self, method: reqwest::Method) {
self.method = method;
}
async fn prepare_response(self) -> Result<(reqwest::Response, String, u8), Error> {
let mut req_builder = reqwest::Client::new().request(
self.method,
reqwest::Url::parse(&self.config.url)
.unwrap()
.join(&self.endpoint)
.unwrap(),
);
if let Some(p) = &self.payload {
req_builder = req_builder.json(p);
}
if let Some(auth) = self.auth {
req_builder = req_builder.header(
"Authorization",
format!("{} {}", auth.auth_type, auth.payload),
);
}
let r = req_builder.send().await.map_err(Error::Request)?;
let status = r.status().as_u16();
if status != 200 {
return Err(Error::HTTPNotOk(status));
}
let headers = r.headers();
if !headers.contains_key(HEADER_RESPONSE_STATUS)
|| !headers.contains_key(HEADER_RESPONSE_MESSAGE)
{
return Err(Error::InvalidHeaders);
}
let msg = headers
.get(HEADER_RESPONSE_MESSAGE)
.unwrap()
.to_str()
.unwrap()
.to_string();
let status: u8 = headers
.get(HEADER_RESPONSE_STATUS)
.unwrap()
.to_str()
.unwrap()
.parse()
.map_err(|_| Error::InvalidHeaders)?;
Ok((r, msg, status))
}
pub async fn do_request_void(self) -> Result<(), Error> {
let (_, msg, status) = self.prepare_response().await?;
if status == STATUS_SUCCESS {
Ok(())
} else {
Err(Error::Error(msg))
}
}
pub async fn do_request<U>(self) -> Result<RequestResult<U>, Error>
where
U: DeserializeOwned,
{
let (r, msg, status) = self.prepare_response().await?;
if status == STATUS_SUCCESS {
let response: U = r.json().await.map_err(Error::Decode)?;
Ok(RequestResult {
response: Some(response),
message: msg.to_string(),
status_code: status,
})
} else {
Err(Error::Error(msg))
}
}
}
#[derive(Debug)]
pub struct RequestResult<T>
where
T: DeserializeOwned,
{
pub response: Option<T>,
pub message: String,
pub status_code: u8,
}
#[derive(Default, Serialize)]
pub struct ListJobs {
#[serde(rename(serialize = "l"))]
pub limit: i32,
}
#[derive(Default, Serialize)]
pub struct JobRequest {
#[serde(rename(serialize = "id"))]
pub job_id: u32,
}
#[derive(Default, Serialize)]
pub struct AddJobRequest {
#[serde(rename(serialize = "buildtype"))]
pub job_type: jobs::Type,
pub args: HashMap<String, String>,
#[serde(rename(serialize = "uploadtype"))]
pub upload_type: jobs::UploadType,
#[serde(rename(serialize = "disableccache"))]
pub disable_ccache: bool,
}
#[derive(Serialize, Debug, Default)]
pub struct Credential {
#[serde(rename(serialize = "mid"))]
pub machine_id: String,
pub username: String,
#[serde(rename(serialize = "pass"))]
pub password: String,
}