use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::clients::RestClient;
use crate::rest::{
build_path, get_path, ResourceError, ResourceOperation, ResourcePath, ResourceResponse,
RestResource,
};
use crate::HttpMethod;
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq)]
pub struct Province {
#[serde(skip_serializing)]
pub id: Option<u64>,
#[serde(skip_serializing)]
pub country_id: Option<u64>,
#[serde(skip_serializing)]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub code: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tax_percentage: Option<f64>,
#[serde(skip_serializing)]
pub shipping_zone_id: Option<u64>,
}
impl Province {
pub async fn count_with_parent(
client: &RestClient,
country_id: u64,
_params: Option<ProvinceCountParams>,
) -> Result<u64, ResourceError> {
let mut ids: HashMap<&str, String> = HashMap::new();
ids.insert("country_id", country_id.to_string());
let available_ids: Vec<&str> = ids.keys().copied().collect();
let path = get_path(Self::PATHS, ResourceOperation::Count, &available_ids).ok_or(
ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "count",
},
)?;
let url = build_path(path.template, &ids);
let response = client.get(&url, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
None,
response.request_id(),
));
}
let count = response
.body
.get("count")
.and_then(serde_json::Value::as_u64)
.ok_or_else(|| {
ResourceError::Http(crate::clients::HttpError::Response(
crate::clients::HttpResponseError {
code: response.code,
message: "Missing 'count' in response".to_string(),
error_reference: response.request_id().map(ToString::to_string),
},
))
})?;
Ok(count)
}
pub async fn find_with_parent(
client: &RestClient,
country_id: u64,
id: u64,
_params: Option<ProvinceFindParams>,
) -> Result<ResourceResponse<Self>, ResourceError> {
let mut ids: HashMap<&str, String> = HashMap::new();
ids.insert("country_id", country_id.to_string());
ids.insert("id", id.to_string());
let available_ids: Vec<&str> = ids.keys().copied().collect();
let path = get_path(Self::PATHS, ResourceOperation::Find, &available_ids).ok_or(
ResourceError::PathResolutionFailed {
resource: Self::NAME,
operation: "find",
},
)?;
let url = build_path(path.template, &ids);
let response = client.get(&url, None).await?;
if !response.is_ok() {
return Err(ResourceError::from_http_response(
response.code,
&response.body,
Self::NAME,
Some(&id.to_string()),
response.request_id(),
));
}
let key = Self::resource_key();
ResourceResponse::from_http_response(response, &key)
}
}
impl RestResource for Province {
type Id = u64;
type FindParams = ProvinceFindParams;
type AllParams = ProvinceListParams;
type CountParams = ProvinceCountParams;
const NAME: &'static str = "Province";
const PLURAL: &'static str = "provinces";
const PATHS: &'static [ResourcePath] = &[
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::Find,
&["country_id", "id"],
"countries/{country_id}/provinces/{id}",
),
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::All,
&["country_id"],
"countries/{country_id}/provinces",
),
ResourcePath::new(
HttpMethod::Get,
ResourceOperation::Count,
&["country_id"],
"countries/{country_id}/provinces/count",
),
ResourcePath::new(
HttpMethod::Put,
ResourceOperation::Update,
&["country_id", "id"],
"countries/{country_id}/provinces/{id}",
),
];
fn get_id(&self) -> Option<Self::Id> {
self.id
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProvinceFindParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProvinceListParams {
#[serde(skip_serializing_if = "Option::is_none")]
pub since_id: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fields: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
pub struct ProvinceCountParams {}
#[cfg(test)]
mod tests {
use super::*;
use crate::rest::{get_path, ResourceOperation};
#[test]
fn test_province_serialization() {
let province = Province {
id: Some(224293623),
country_id: Some(879921427),
name: Some("Ontario".to_string()),
code: Some("ON".to_string()),
tax: Some(0.08),
tax_name: Some("HST".to_string()),
tax_type: Some("compounded".to_string()),
tax_percentage: Some(8.0),
shipping_zone_id: Some(123),
};
let json = serde_json::to_string(&province).unwrap();
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(parsed["code"], "ON");
assert_eq!(parsed["tax"], 0.08);
assert_eq!(parsed["tax_name"], "HST");
assert_eq!(parsed["tax_type"], "compounded");
assert_eq!(parsed["tax_percentage"], 8.0);
assert!(parsed.get("id").is_none());
assert!(parsed.get("country_id").is_none());
assert!(parsed.get("name").is_none());
assert!(parsed.get("shipping_zone_id").is_none());
}
#[test]
fn test_province_deserialization() {
let json = r#"{
"id": 224293623,
"country_id": 879921427,
"name": "Ontario",
"code": "ON",
"tax": 0.08,
"tax_name": "HST",
"tax_type": "compounded",
"tax_percentage": 8.0,
"shipping_zone_id": 123456
}"#;
let province: Province = serde_json::from_str(json).unwrap();
assert_eq!(province.id, Some(224293623));
assert_eq!(province.country_id, Some(879921427));
assert_eq!(province.name, Some("Ontario".to_string()));
assert_eq!(province.code, Some("ON".to_string()));
assert_eq!(province.tax, Some(0.08));
assert_eq!(province.tax_name, Some("HST".to_string()));
assert_eq!(province.tax_type, Some("compounded".to_string()));
assert_eq!(province.tax_percentage, Some(8.0));
assert_eq!(province.shipping_zone_id, Some(123456));
}
#[test]
fn test_province_nested_paths_no_create_delete() {
let find_path = get_path(
Province::PATHS,
ResourceOperation::Find,
&["country_id", "id"],
);
assert!(find_path.is_some());
assert_eq!(
find_path.unwrap().template,
"countries/{country_id}/provinces/{id}"
);
let all_path = get_path(Province::PATHS, ResourceOperation::All, &["country_id"]);
assert!(all_path.is_some());
assert_eq!(
all_path.unwrap().template,
"countries/{country_id}/provinces"
);
let count_path = get_path(Province::PATHS, ResourceOperation::Count, &["country_id"]);
assert!(count_path.is_some());
assert_eq!(
count_path.unwrap().template,
"countries/{country_id}/provinces/count"
);
let update_path = get_path(
Province::PATHS,
ResourceOperation::Update,
&["country_id", "id"],
);
assert!(update_path.is_some());
assert_eq!(
update_path.unwrap().template,
"countries/{country_id}/provinces/{id}"
);
let create_path = get_path(Province::PATHS, ResourceOperation::Create, &["country_id"]);
assert!(create_path.is_none());
let delete_path = get_path(
Province::PATHS,
ResourceOperation::Delete,
&["country_id", "id"],
);
assert!(delete_path.is_none());
}
#[test]
fn test_province_constants() {
assert_eq!(Province::NAME, "Province");
assert_eq!(Province::PLURAL, "provinces");
}
#[test]
fn test_province_get_id() {
let province_with_id = Province {
id: Some(224293623),
code: Some("ON".to_string()),
..Default::default()
};
assert_eq!(province_with_id.get_id(), Some(224293623));
let province_without_id = Province::default();
assert_eq!(province_without_id.get_id(), None);
}
}