pub mod commands; use http::header::CONTENT_TYPE; use http::{HeaderMap, HeaderValue}; use serde::de::DeserializeOwned; use serde::Serialize; use std::fmt::Debug; use thiserror::Error; use url::{ParseError, Url}; pub struct JiraClient { pub url: String, pub email: String, pub api_token: String, } pub trait JiraCommand { type TResponse: DeserializeOwned + Clone; type TPayload: Serialize; const REQUEST_TYPE: JiraRequestType; fn endpoint(&self) -> String; fn request_body(&self) -> Option<&Self::TPayload>; fn query_params(&self) -> Option>; } pub enum JiraRequestType { Read, Create, Update, Delete, } impl From for http::Method { fn from(value: JiraRequestType) -> Self { match value { JiraRequestType::Read => Self::GET, JiraRequestType::Create => Self::POST, JiraRequestType::Update => Self::PATCH, JiraRequestType::Delete => Self::DELETE, } } } #[derive(Debug, Error)] pub enum JiraClientError { #[error(transparent)] UrlParseError(#[from] ParseError), #[error(transparent)] ReqwestError(#[from] reqwest::Error), #[error("API Error: {0}")] ApiError(String), } impl JiraClient { pub fn new(url: &str, email: &str, api_token: &str) -> Self { Self { url: url.to_string(), email: email.to_string(), api_token: api_token.to_string(), } } pub async fn exec( &self, cmd: TCommand, ) -> Result where TCommand: JiraCommand + Debug, { let client = reqwest::Client::new(); let mut headers = HeaderMap::new(); headers.insert(CONTENT_TYPE, HeaderValue::from_static("application/json")); let url = self.construct_url(&cmd.endpoint())?; let mut request_builder = client .request(TCommand::REQUEST_TYPE.into(), url) .basic_auth(&self.email, Some(&self.api_token)) .headers(headers); if let Some(params) = cmd.query_params() { request_builder = request_builder.query(¶ms); } if let Some(body) = cmd.request_body() { request_builder = request_builder.json(body); } let response = match request_builder.send().await { Ok(x) => x, Err(reason) => return Err(JiraClientError::ReqwestError(reason)), }; if !response.status().is_success() { let error_text = response.text().await?; return Err(JiraClientError::ApiError(error_text)); } match response.json::().await { Ok(x) => Ok(x), Err(reason) => Err(JiraClientError::ReqwestError(reason)), } } fn construct_url(&self, endpoint: &str) -> Result { let mut url = match Url::parse(&self.url) { Ok(x) => x, Err(reason) => { return Err(JiraClientError::UrlParseError(reason)); } }; url.set_path(endpoint); Ok(url.to_string()) } }