use crate::v1::{Client, error, BASE_URL};
use serde::{Deserialize, Serialize};
#[derive(Deserialize, Debug)]
pub struct ListCategoriesResponse {
pub data : Vec<CategoryResource>,
}
#[derive(Deserialize, Debug)]
pub struct GetCategoryResponse {
pub data : CategoryResource,
}
#[derive(Deserialize, Debug)]
pub struct CategoryResource {
pub r#type : String,
pub id : String,
pub attributes : Attributes,
pub relationships : Relationships,
pub links : Option<CategoryResourceLinks>,
}
#[derive(Deserialize, Debug)]
pub struct Attributes {
pub name : String,
}
#[derive(Deserialize, Debug)]
pub struct Relationships {
pub parent : Parent,
pub children : Children,
}
#[derive(Deserialize, Debug)]
pub struct Parent {
pub data : Option<ParentData>,
pub links : Option<ParentLinks>,
}
#[derive(Deserialize, Debug)]
pub struct ParentData {
pub r#type : String,
pub id : String,
}
#[derive(Deserialize, Debug)]
pub struct ParentLinks {
pub related : String,
}
#[derive(Deserialize, Debug)]
pub struct Children {
pub data : Vec<ChildrenData>,
pub links : Option<ChildrenLinks>,
}
#[derive(Deserialize, Debug)]
pub struct ChildrenData {
pub r#type : String,
pub id : String,
}
#[derive(Deserialize, Debug)]
pub struct ChildrenLinks {
pub related : String,
}
#[derive(Deserialize, Debug)]
pub struct CategoryResourceLinks {
#[serde(rename = "self")]
pub this : String,
}
#[derive(Default)]
pub struct ListCategoriesOptions {
filter_parent : Option<String>,
}
impl ListCategoriesOptions {
pub fn filter_parent(&mut self, value : String) {
self.filter_parent = Some(value);
}
fn add_params(&self, url : &mut reqwest::Url) {
let mut query = String::new();
if let Some(value) = &self.filter_parent {
if !query.is_empty() {
query.push('&');
}
query.push_str(&format!("filter[parent]={}", value));
}
if !query.is_empty() {
url.set_query(Some(&query));
}
}
}
#[derive(Serialize)]
struct CategoriseTransactionRequest {
data : Option<CategoryInputResourceIdentifier>,
}
#[derive(Serialize)]
struct CategoryInputResourceIdentifier {
r#type : String,
id : String,
}
impl Client {
pub async fn list_categories(&self, options : &ListCategoriesOptions) -> Result<ListCategoriesResponse, error::Error> {
let mut url = reqwest::Url::parse(&format!("{}/categories", BASE_URL)).map_err(error::Error::UrlParse)?;
options.add_params(&mut url);
let res = reqwest::Client::new()
.get(url)
.header("Authorization", self.auth_header())
.send()
.await
.map_err(error::Error::Request)?;
match res.status() {
reqwest::StatusCode::OK => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let category_response : ListCategoriesResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
Ok(category_response)
},
_ => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let error : error::ErrorResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
Err(error::Error::Api(error))
}
}
}
pub async fn get_category(&self, id : &str) -> Result<GetCategoryResponse, error::Error> {
if id.is_empty() {
panic!("The provided category ID must not be empty.");
}
let url = reqwest::Url::parse(&format!("{}/categories/{}", BASE_URL, id)).map_err(error::Error::UrlParse)?;
let res = reqwest::Client::new()
.get(url)
.header("Authorization", self.auth_header())
.send()
.await
.map_err(error::Error::Request)?;
match res.status() {
reqwest::StatusCode::OK => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let category_response : GetCategoryResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
Ok(category_response )
},
_ => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let error : error::ErrorResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
Err(error::Error::Api(error))
}
}
}
pub async fn categorise_transaction(&self, transaction_id : &str, category : Option<&str>) -> Result<(), error::Error> {
let url = reqwest::Url::parse(&format!("{}/transactions/{}/relationships/category", BASE_URL, transaction_id)).map_err(error::Error::UrlParse)?;
let category = category.map(|id| {
CategoryInputResourceIdentifier {
r#type : String::from("categories"),
id : String::from(id),
}
});
let body = CategoriseTransactionRequest { data : category };
let body = serde_json::to_string(&body).map_err(error::Error::Serialize)?;
println!("{}", body);
let res = reqwest::Client::new()
.patch(url)
.header("Authorization", self.auth_header())
.header("Content-Type", "application/json")
.body(body)
.send()
.await
.map_err(error::Error::Request)?;
match res.status() {
reqwest::StatusCode::NO_CONTENT => {
Ok(())
},
_ => {
let body = res.text().await.map_err(error::Error::BodyRead)?;
let error : error::ErrorResponse = serde_json::from_str(&body).map_err(error::Error::Json)?;
Err(error::Error::Api(error))
}
}
}
}