1use crate::client::Client;
6use crate::error::{Error, Result};
7use crate::internal::apply_pagination;
8use crate::pagination::{FetchFn, Page, PageStream};
9use crate::resources::agencies::urlencoding;
10use crate::Record;
11use bon::Builder;
12use std::sync::Arc;
13
14#[derive(Debug, Clone, Default, Builder, PartialEq, Eq)]
18#[non_exhaustive]
19pub struct ListVehicleAwardeesOptions {
20 #[builder(into)]
22 pub page: Option<u32>,
23 #[builder(into)]
25 pub limit: Option<u32>,
26 #[builder(into)]
28 pub cursor: Option<String>,
29 #[builder(into)]
32 pub shape: Option<String>,
33 #[builder(default)]
35 pub flat: bool,
36 #[builder(default)]
38 pub flat_lists: bool,
39}
40
41impl ListVehicleAwardeesOptions {
42 fn to_query(&self) -> Vec<(String, String)> {
43 let mut q = Vec::new();
44 apply_pagination(
45 &mut q,
46 self.page,
47 self.limit,
48 self.cursor.as_deref(),
49 self.shape.as_deref(),
50 self.flat,
51 self.flat_lists,
52 );
53 q
54 }
55}
56
57#[derive(Debug, Clone, Default, Builder, PartialEq, Eq)]
61#[non_exhaustive]
62pub struct ListVehicleOrdersOptions {
63 #[builder(into)]
65 pub page: Option<u32>,
66 #[builder(into)]
68 pub limit: Option<u32>,
69 #[builder(into)]
71 pub cursor: Option<String>,
72 #[builder(into)]
74 pub shape: Option<String>,
75 #[builder(default)]
77 pub flat: bool,
78 #[builder(default)]
80 pub flat_lists: bool,
81}
82
83impl ListVehicleOrdersOptions {
84 fn to_query(&self) -> Vec<(String, String)> {
85 let mut q = Vec::new();
86 apply_pagination(
87 &mut q,
88 self.page,
89 self.limit,
90 self.cursor.as_deref(),
91 self.shape.as_deref(),
92 self.flat,
93 self.flat_lists,
94 );
95 q
96 }
97}
98
99impl Client {
100 pub async fn list_vehicle_awardees(
103 &self,
104 uuid: &str,
105 opts: ListVehicleAwardeesOptions,
106 ) -> Result<Page<Record>> {
107 list_vehicle_awardees_inner(self, uuid, opts).await
108 }
109
110 pub async fn list_vehicle_orders(
113 &self,
114 uuid: &str,
115 opts: ListVehicleOrdersOptions,
116 ) -> Result<Page<Record>> {
117 list_vehicle_orders_inner(self, uuid, opts).await
118 }
119
120 pub fn iterate_vehicle_awardees(
122 &self,
123 uuid: &str,
124 opts: ListVehicleAwardeesOptions,
125 ) -> PageStream<Record> {
126 let opts = Arc::new(opts);
127 let uuid = Arc::new(uuid.to_string());
128 let fetch: FetchFn<Record> = Box::new(move |client, page, cursor| {
129 let mut next = (*opts).clone();
130 next.page = page;
131 next.cursor = cursor;
132 let uuid = uuid.clone();
133 Box::pin(async move { list_vehicle_awardees_inner(&client, &uuid, next).await })
134 });
135 PageStream::new(self.clone(), fetch)
136 }
137
138 pub fn iterate_vehicle_orders(
140 &self,
141 uuid: &str,
142 opts: ListVehicleOrdersOptions,
143 ) -> PageStream<Record> {
144 let opts = Arc::new(opts);
145 let uuid = Arc::new(uuid.to_string());
146 let fetch: FetchFn<Record> = Box::new(move |client, page, cursor| {
147 let mut next = (*opts).clone();
148 next.page = page;
149 next.cursor = cursor;
150 let uuid = uuid.clone();
151 Box::pin(async move { list_vehicle_orders_inner(&client, &uuid, next).await })
152 });
153 PageStream::new(self.clone(), fetch)
154 }
155}
156
157async fn list_vehicle_awardees_inner(
158 client: &Client,
159 uuid: &str,
160 opts: ListVehicleAwardeesOptions,
161) -> Result<Page<Record>> {
162 if uuid.is_empty() {
163 return Err(Error::Validation {
164 message: "list_vehicle_awardees: uuid is required".into(),
165 response: None,
166 });
167 }
168 let q = opts.to_query();
169 let path = format!("/api/vehicles/{}/awardees/", urlencoding(uuid));
170 let bytes = client.get_bytes(&path, &q).await?;
171 Page::decode(&bytes)
172}
173
174async fn list_vehicle_orders_inner(
175 client: &Client,
176 uuid: &str,
177 opts: ListVehicleOrdersOptions,
178) -> Result<Page<Record>> {
179 if uuid.is_empty() {
180 return Err(Error::Validation {
181 message: "list_vehicle_orders: uuid is required".into(),
182 response: None,
183 });
184 }
185 let q = opts.to_query();
186 let path = format!("/api/vehicles/{}/orders/", urlencoding(uuid));
187 let bytes = client.get_bytes(&path, &q).await?;
188 Page::decode(&bytes)
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn awardees_opts_emit_pagination() {
197 let opts = ListVehicleAwardeesOptions::builder()
198 .page(2u32)
199 .limit(25u32)
200 .shape("vehicle_awardees(minimal)")
201 .build();
202 let q = opts.to_query();
203 assert!(q.contains(&("page".into(), "2".into())));
204 assert!(q.contains(&("limit".into(), "25".into())));
205 assert!(q.contains(&("shape".into(), "vehicle_awardees(minimal)".into())));
206 }
207
208 #[test]
209 fn orders_opts_emit_flat() {
210 let opts = ListVehicleOrdersOptions::builder()
211 .flat(true)
212 .flat_lists(true)
213 .build();
214 let q = opts.to_query();
215 assert!(q.contains(&("flat".into(), "true".into())));
216 assert!(q.contains(&("flat_lists".into(), "true".into())));
217 }
218
219 #[tokio::test]
220 async fn list_vehicle_awardees_empty_uuid_returns_validation() {
221 let client = Client::builder().api_key("x").build().expect("build");
222 let err = client
223 .list_vehicle_awardees("", ListVehicleAwardeesOptions::default())
224 .await
225 .expect_err("must error");
226 match err {
227 Error::Validation { message, .. } => {
228 assert!(message.contains("uuid"));
229 }
230 other => panic!("expected Validation, got {other:?}"),
231 }
232 }
233
234 #[tokio::test]
235 async fn list_vehicle_orders_empty_uuid_returns_validation() {
236 let client = Client::builder().api_key("x").build().expect("build");
237 let err = client
238 .list_vehicle_orders("", ListVehicleOrdersOptions::default())
239 .await
240 .expect_err("must error");
241 match err {
242 Error::Validation { message, .. } => {
243 assert!(message.contains("uuid"));
244 }
245 other => panic!("expected Validation, got {other:?}"),
246 }
247 }
248}