ridewithgps_client/
collections.rs1use crate::{PaginatedResponse, Result, RideWithGpsClient, Route, Trip};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct Collection {
9 pub id: u64,
11
12 pub name: Option<String>,
14
15 pub description: Option<String>,
17
18 pub user_id: Option<u64>,
20
21 pub visibility: Option<crate::Visibility>,
23
24 pub url: Option<String>,
26
27 pub html_url: Option<String>,
29
30 pub cover: Option<String>,
32
33 pub created_at: Option<String>,
35
36 pub updated_at: Option<String>,
38
39 pub route_count: Option<u32>,
41
42 pub cover_photo_url: Option<String>,
44
45 pub routes: Option<Vec<Route>>,
47
48 pub trips: Option<Vec<Trip>>,
50}
51
52#[derive(Debug, Clone, Default, Serialize)]
54pub struct ListCollectionsParams {
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub name: Option<String>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub page: Option<u32>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub page_size: Option<u32>,
66}
67
68impl RideWithGpsClient {
69 pub fn list_collections(
90 &self,
91 params: Option<&ListCollectionsParams>,
92 ) -> Result<PaginatedResponse<Collection>> {
93 let mut url = "/api/v1/collections.json".to_string();
94
95 if let Some(params) = params {
96 let query = serde_json::to_value(params)?;
97 if let Some(obj) = query.as_object() {
98 if !obj.is_empty() {
99 let query_str = serde_urlencoded::to_string(obj).map_err(|e| {
100 crate::Error::ApiError(format!("Failed to encode query: {}", e))
101 })?;
102 url.push('?');
103 url.push_str(&query_str);
104 }
105 }
106 }
107
108 self.get(&url)
109 }
110
111 pub fn get_collection(&self, id: u64) -> Result<Collection> {
148 #[derive(Deserialize)]
149 struct CollectionWrapper {
150 collection: Collection,
151 }
152
153 let wrapper: CollectionWrapper = self.get(&format!("/api/v1/collections/{}.json", id))?;
154 Ok(wrapper.collection)
155 }
156
157 pub fn get_pinned_collection(&self) -> Result<Collection> {
174 #[derive(Deserialize)]
175 struct CollectionWrapper {
176 collection: Collection,
177 }
178
179 let wrapper: CollectionWrapper = self.get("/api/v1/collections/pinned.json")?;
180 Ok(wrapper.collection)
181 }
182}
183
184#[cfg(test)]
185mod tests {
186 use super::*;
187
188 #[test]
189 fn test_collection_deserialization() {
190 let json = r#"{
191 "id": 999,
192 "name": "My Favorite Routes",
193 "description": "Best rides in the area",
194 "user_id": 123,
195 "route_count": 15
196 }"#;
197
198 let collection: Collection = serde_json::from_str(json).unwrap();
199 assert_eq!(collection.id, 999);
200 assert_eq!(collection.name.as_deref(), Some("My Favorite Routes"));
201 assert_eq!(
202 collection.description.as_deref(),
203 Some("Best rides in the area")
204 );
205 assert_eq!(collection.route_count, Some(15));
206 }
207
208 #[test]
209 fn test_list_collections_params() {
210 let params = ListCollectionsParams {
211 name: Some("favorites".to_string()),
212 page: Some(1),
213 ..Default::default()
214 };
215
216 let json = serde_json::to_value(¶ms).unwrap();
217 assert!(json.get("name").is_some());
218 assert!(json.get("page").is_some());
219 }
220
221 #[test]
222 fn test_collection_wrapper_deserialization() {
223 let json = r#"{
224 "collection": {
225 "id": 888,
226 "name": "Wrapped Collection",
227 "visibility": "public"
228 }
229 }"#;
230
231 #[derive(Deserialize)]
232 struct CollectionWrapper {
233 collection: Collection,
234 }
235
236 let wrapper: CollectionWrapper = serde_json::from_str(json).unwrap();
237 assert_eq!(wrapper.collection.id, 888);
238 assert_eq!(
239 wrapper.collection.name.as_deref(),
240 Some("Wrapped Collection")
241 );
242 assert_eq!(
243 wrapper.collection.visibility,
244 Some(crate::Visibility::Public)
245 );
246 }
247
248 #[test]
249 fn test_collection_with_nested_routes() {
250 let json = r#"{
251 "id": 777,
252 "name": "Collection with Routes",
253 "routes": [
254 {
255 "id": 1,
256 "name": "Route 1",
257 "distance": 10000.0
258 },
259 {
260 "id": 2,
261 "name": "Route 2",
262 "distance": 20000.0
263 }
264 ]
265 }"#;
266
267 let collection: Collection = serde_json::from_str(json).unwrap();
268 assert_eq!(collection.id, 777);
269 assert!(collection.routes.is_some());
270 let routes = collection.routes.unwrap();
271 assert_eq!(routes.len(), 2);
272 assert_eq!(routes[0].id, 1);
273 assert_eq!(routes[0].name.as_deref(), Some("Route 1"));
274 assert_eq!(routes[1].id, 2);
275 }
276
277 #[test]
278 fn test_collection_with_nested_trips() {
279 let json = r#"{
280 "id": 666,
281 "name": "Collection with Trips",
282 "trips": [
283 {
284 "id": 10,
285 "name": "Trip 1",
286 "distance": 15000.0
287 }
288 ]
289 }"#;
290
291 let collection: Collection = serde_json::from_str(json).unwrap();
292 assert_eq!(collection.id, 666);
293 assert!(collection.trips.is_some());
294 let trips = collection.trips.unwrap();
295 assert_eq!(trips.len(), 1);
296 assert_eq!(trips[0].id, 10);
297 assert_eq!(trips[0].name.as_deref(), Some("Trip 1"));
298 }
299}