1use crate::client::Client;
14use crate::error::{Error, Result};
15use crate::resources::agencies::urlencoding;
16use crate::Record;
17
18pub const METRICS_OWNER_NAICS: &str = "naics";
20pub const METRICS_OWNER_PSC: &str = "psc";
22pub const METRICS_OWNER_ENTITY: &str = "entity";
24
25#[derive(Debug, Clone, Default, PartialEq, Eq)]
33#[non_exhaustive]
34pub struct ListMetricsOptions {
35 pub owner_type: String,
38 pub owner_id: String,
40 pub months: u32,
42 pub period_grouping: String,
44}
45
46impl ListMetricsOptions {
47 #[must_use]
49 pub fn new(
50 owner_type: impl Into<String>,
51 owner_id: impl Into<String>,
52 months: u32,
53 period_grouping: impl Into<String>,
54 ) -> Self {
55 Self {
56 owner_type: owner_type.into(),
57 owner_id: owner_id.into(),
58 months,
59 period_grouping: period_grouping.into(),
60 }
61 }
62}
63
64impl Client {
65 pub async fn get_naics_metrics(
69 &self,
70 code: &str,
71 months: u32,
72 period_grouping: &str,
73 ) -> Result<Record> {
74 if code.is_empty() {
75 return Err(Error::Validation {
76 message: "get_naics_metrics: NAICS code is required".into(),
77 response: None,
78 });
79 }
80 if months == 0 {
81 return Err(Error::Validation {
82 message: "get_naics_metrics: months must be > 0".into(),
83 response: None,
84 });
85 }
86 if period_grouping.is_empty() {
87 return Err(Error::Validation {
88 message: "get_naics_metrics: period_grouping is required".into(),
89 response: None,
90 });
91 }
92 let path = format!(
93 "/api/naics/{}/metrics/{}/{}/",
94 urlencoding(code),
95 months,
96 urlencoding(period_grouping),
97 );
98 self.get_json::<Record>(&path, &[]).await
99 }
100
101 pub async fn get_psc_metrics(
105 &self,
106 code: &str,
107 months: u32,
108 period_grouping: &str,
109 ) -> Result<Record> {
110 if code.is_empty() {
111 return Err(Error::Validation {
112 message: "get_psc_metrics: PSC code is required".into(),
113 response: None,
114 });
115 }
116 if months == 0 {
117 return Err(Error::Validation {
118 message: "get_psc_metrics: months must be > 0".into(),
119 response: None,
120 });
121 }
122 if period_grouping.is_empty() {
123 return Err(Error::Validation {
124 message: "get_psc_metrics: period_grouping is required".into(),
125 response: None,
126 });
127 }
128 let path = format!(
129 "/api/psc/{}/metrics/{}/{}/",
130 urlencoding(code),
131 months,
132 urlencoding(period_grouping),
133 );
134 self.get_json::<Record>(&path, &[]).await
135 }
136
137 pub async fn list_metrics(&self, opts: ListMetricsOptions) -> Result<Record> {
151 if opts.owner_id.is_empty() {
152 return Err(Error::Validation {
153 message: "list_metrics: owner_id is required".into(),
154 response: None,
155 });
156 }
157 if opts.months == 0 {
158 return Err(Error::Validation {
159 message: "list_metrics: months must be > 0".into(),
160 response: None,
161 });
162 }
163 if opts.period_grouping.is_empty() {
164 return Err(Error::Validation {
165 message: "list_metrics: period_grouping is required".into(),
166 response: None,
167 });
168 }
169 match opts.owner_type.as_str() {
170 METRICS_OWNER_NAICS => {
171 self.get_naics_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
172 .await
173 }
174 METRICS_OWNER_PSC => {
175 self.get_psc_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
176 .await
177 }
178 METRICS_OWNER_ENTITY => {
179 self.get_entity_metrics(&opts.owner_id, opts.months, &opts.period_grouping)
180 .await
181 }
182 _ => Err(Error::Validation {
183 message: "list_metrics: owner_type must be one of: naics, psc, entity".into(),
184 response: None,
185 }),
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[tokio::test]
195 async fn naics_metrics_empty_code_is_validation() {
196 let c = Client::builder().api_key("k").build().expect("client");
197 let err = c
198 .get_naics_metrics("", 12, "month")
199 .await
200 .expect_err("must error");
201 assert!(matches!(err, Error::Validation { .. }));
202 }
203
204 #[tokio::test]
205 async fn naics_metrics_zero_months_is_validation() {
206 let c = Client::builder().api_key("k").build().expect("client");
207 let err = c
208 .get_naics_metrics("541512", 0, "month")
209 .await
210 .expect_err("must error");
211 assert!(matches!(err, Error::Validation { .. }));
212 }
213
214 #[tokio::test]
215 async fn naics_metrics_empty_grouping_is_validation() {
216 let c = Client::builder().api_key("k").build().expect("client");
217 let err = c
218 .get_naics_metrics("541512", 12, "")
219 .await
220 .expect_err("must error");
221 assert!(matches!(err, Error::Validation { .. }));
222 }
223
224 #[tokio::test]
225 async fn psc_metrics_empty_code_is_validation() {
226 let c = Client::builder().api_key("k").build().expect("client");
227 let err = c
228 .get_psc_metrics("", 12, "month")
229 .await
230 .expect_err("must error");
231 assert!(matches!(err, Error::Validation { .. }));
232 }
233
234 #[tokio::test]
235 async fn psc_metrics_zero_months_is_validation() {
236 let c = Client::builder().api_key("k").build().expect("client");
237 let err = c
238 .get_psc_metrics("D302", 0, "month")
239 .await
240 .expect_err("must error");
241 assert!(matches!(err, Error::Validation { .. }));
242 }
243
244 #[tokio::test]
245 async fn list_metrics_empty_owner_id_is_validation() {
246 let c = Client::builder().api_key("k").build().expect("client");
247 let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "", 12, "month");
248 let err = c.list_metrics(opts).await.expect_err("must error");
249 let msg = match &err {
250 Error::Validation { message, .. } => message.clone(),
251 other => panic!("expected Validation, got {other:?}"),
252 };
253 assert!(msg.contains("owner_id"), "got: {msg}");
254 }
255
256 #[tokio::test]
257 async fn list_metrics_zero_months_is_validation() {
258 let c = Client::builder().api_key("k").build().expect("client");
259 let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "541512", 0, "month");
260 let err = c.list_metrics(opts).await.expect_err("must error");
261 let msg = match &err {
262 Error::Validation { message, .. } => message.clone(),
263 other => panic!("expected Validation, got {other:?}"),
264 };
265 assert!(msg.contains("months"), "got: {msg}");
266 }
267
268 #[tokio::test]
269 async fn list_metrics_empty_grouping_is_validation() {
270 let c = Client::builder().api_key("k").build().expect("client");
271 let opts = ListMetricsOptions::new(METRICS_OWNER_NAICS, "541512", 12, "");
272 let err = c.list_metrics(opts).await.expect_err("must error");
273 let msg = match &err {
274 Error::Validation { message, .. } => message.clone(),
275 other => panic!("expected Validation, got {other:?}"),
276 };
277 assert!(msg.contains("period_grouping"), "got: {msg}");
278 }
279
280 #[tokio::test]
281 async fn list_metrics_unknown_owner_is_validation() {
282 let c = Client::builder().api_key("k").build().expect("client");
283 let opts = ListMetricsOptions::new("bogus", "541512", 12, "month");
284 let err = c.list_metrics(opts).await.expect_err("must error");
285 let msg = match &err {
286 Error::Validation { message, .. } => message.clone(),
287 other => panic!("expected Validation, got {other:?}"),
288 };
289 assert!(msg.contains("owner_type"), "got: {msg}");
290 }
291}