use crate::client::Client;
use crate::error::{Error, Result};
use crate::resources::agencies::urlencoding;
use crate::Record;
pub const METRICS_OWNER_NAICS: &str = "naics";
pub const METRICS_OWNER_PSC: &str = "psc";
pub const METRICS_OWNER_ENTITY: &str = "entity";
#[derive(Debug, Clone, Default, PartialEq, Eq)]
#[non_exhaustive]
pub struct ListMetricsOptions {
pub owner_type: String,
pub owner_id: String,
pub months: u32,
pub period_grouping: String,
}
impl ListMetricsOptions {
#[must_use]
pub fn new(
owner_type: impl Into<String>,
owner_id: impl Into<String>,
months: u32,
period_grouping: impl Into<String>,
) -> Self {
Self {
owner_type: owner_type.into(),
owner_id: owner_id.into(),
months,
period_grouping: period_grouping.into(),
}
}
}
impl Client {
pub async fn get_naics_metrics(
&self,
code: &str,
months: u32,
period_grouping: &str,
) -> Result<Record> {
if code.is_empty() {
return Err(Error::Validation {
message: "get_naics_metrics: NAICS code is required".into(),
response: None,
});
}
if months == 0 {
return Err(Error::Validation {
message: "get_naics_metrics: months must be > 0".into(),
response: None,
});
}
if period_grouping.is_empty() {
return Err(Error::Validation {
message: "get_naics_metrics: period_grouping is required".into(),
response: None,
});
}
let path = format!(
"/api/naics/{}/metrics/{}/{}/",
urlencoding(code),
months,
urlencoding(period_grouping),
);
self.get_json::<Record>(&path, &[]).await
}
pub async fn get_psc_metrics(
&self,
code: &str,
months: u32,
period_grouping: &str,
) -> Result<Record> {
if code.is_empty() {
return Err(Error::Validation {
message: "get_psc_metrics: PSC code is required".into(),
response: None,
});
}
if months == 0 {
return Err(Error::Validation {
message: "get_psc_metrics: months must be > 0".into(),
response: None,
});
}
if period_grouping.is_empty() {
return Err(Error::Validation {
message: "get_psc_metrics: period_grouping is required".into(),
response: None,
});
}
let path = format!(
"/api/psc/{}/metrics/{}/{}/",
urlencoding(code),
months,
urlencoding(period_grouping),
);
self.get_json::<Record>(&path, &[]).await
}
pub async fn list_metrics(&self, opts: ListMetricsOptions) -> Result<Record> {
if opts.owner_id.is_empty() {
return Err(Error::Validation {
message: "list_metrics: owner_id is required".into(),
response: None,
});
}
if opts.months == 0 {
return Err(Error::Validation {
message: "list_metrics: months must be > 0".into(),
response: None,
});
}
if opts.period_grouping.is_empty() {
return Err(Error::Validation {
message: "list_metrics: period_grouping is required".into(),
response: None,
});
}
match opts.owner_type.as_str() {
METRICS_OWNER_NAICS => {
self.get_naics_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
.await
}
METRICS_OWNER_PSC => {
self.get_psc_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
.await
}
METRICS_OWNER_ENTITY => {
self.get_entity_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
.await
}
_ => Err(Error::Validation {
message: "list_metrics: owner_type must be one of: naics, psc, entity".into(),
response: None,
}),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn naics_metrics_empty_code_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let err = c
.get_naics_metrics("", 12, "month")
.await
.expect_err("must error");
assert!(matches!(err, Error::Validation { .. }));
}
#[tokio::test]
async fn naics_metrics_zero_months_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let err = c
.get_naics_metrics("541512", 0, "month")
.await
.expect_err("must error");
assert!(matches!(err, Error::Validation { .. }));
}
#[tokio::test]
async fn naics_metrics_empty_grouping_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let err = c
.get_naics_metrics("541512", 12, "")
.await
.expect_err("must error");
assert!(matches!(err, Error::Validation { .. }));
}
#[tokio::test]
async fn psc_metrics_empty_code_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let err = c
.get_psc_metrics("", 12, "month")
.await
.expect_err("must error");
assert!(matches!(err, Error::Validation { .. }));
}
#[tokio::test]
async fn psc_metrics_zero_months_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let err = c
.get_psc_metrics("D302", 0, "month")
.await
.expect_err("must error");
assert!(matches!(err, Error::Validation { .. }));
}
#[tokio::test]
async fn list_metrics_empty_owner_id_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "", 12, "month");
let err = c.list_metrics(opts).await.expect_err("must error");
let msg = match &err {
Error::Validation { message, .. } => message.clone(),
other => panic!("expected Validation, got {other:?}"),
};
assert!(msg.contains("owner_id"), "got: {msg}");
}
#[tokio::test]
async fn list_metrics_zero_months_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "541512", 0, "month");
let err = c.list_metrics(opts).await.expect_err("must error");
let msg = match &err {
Error::Validation { message, .. } => message.clone(),
other => panic!("expected Validation, got {other:?}"),
};
assert!(msg.contains("months"), "got: {msg}");
}
#[tokio::test]
async fn list_metrics_empty_grouping_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "541512", 12, "");
let err = c.list_metrics(opts).await.expect_err("must error");
let msg = match &err {
Error::Validation { message, .. } => message.clone(),
other => panic!("expected Validation, got {other:?}"),
};
assert!(msg.contains("period_grouping"), "got: {msg}");
}
#[tokio::test]
async fn list_metrics_unknown_owner_is_validation() {
let c = Client::builder().api_key("k").build().expect("client");
let opts = ListMetricsOptions::new("bogus", "541512", 12, "month");
let err = c.list_metrics(opts).await.expect_err("must error");
let msg = match &err {
Error::Validation { message, .. } => message.clone(),
other => panic!("expected Validation, got {other:?}"),
};
assert!(msg.contains("owner_type"), "got: {msg}");
}
}