fjordgard

A desktop clock application
Log | Files | Refs | README | LICENSE

commit 95c697219ede6098660ac440625193248985f907
parent c9eecd89fe484ea0d83f0d1ac144dbb3c745e041
Author: Sylvia Ivory <git@sivory.net>
Date:   Tue, 17 Jun 2025 02:24:03 -0700

Allow reading pagination information in unsplash

Diffstat:
Mcrates/unsplash/src/error.rs | 4++++
Mcrates/unsplash/src/lib.rs | 43+++++++++++++++++++++++++++++++++++++------
Mcrates/unsplash/src/model.rs | 7+++++++
3 files changed, 48 insertions(+), 6 deletions(-)

diff --git a/crates/unsplash/src/error.rs b/crates/unsplash/src/error.rs @@ -8,6 +8,10 @@ pub enum Error { Unsplash(String), #[error("json: {0}")] SerdeJson(#[from] serde_json::Error), + #[error("unsplash response missing header: {0}")] + MissingHeader(&'static str), + #[error("unsplash response malformed")] + MalformedResponse, } pub type Result<T, E = Error> = std::result::Result<T, E>; diff --git a/crates/unsplash/src/lib.rs b/crates/unsplash/src/lib.rs @@ -41,7 +41,7 @@ impl UnsplashClient { &self, route: &str, query: Option<Q>, - ) -> Result<T> { + ) -> Result<(T, HeaderMap)> { let mut req = self.client.get(format!("{UNSPLASH_API_HOST}/{route}")); if let Some(ref query) = query { @@ -54,12 +54,13 @@ impl UnsplashClient { return Err(Error::InvalidAPIKey); } + let headers = res.headers().clone(); let body: UnsplashResponse = res.json().await?; match body { UnsplashResponse::Error { errors } => Err(Error::Unsplash(errors.join(", "))), UnsplashResponse::Success(v) => match serde_json::from_value(v) { - Ok(o) => Ok(o), + Ok(o) => Ok((o, headers)), Err(e) => Err(Error::SerdeJson(e)), }, } @@ -70,8 +71,28 @@ impl UnsplashClient { &self, id: &str, opt: Option<CollectionPhotosOptions>, - ) -> Result<Vec<Photo>> { - self.request(&format!("collections/{id}/photos"), opt).await + ) -> Result<CollectionPhotos> { + let (photos, headers) = self + .request(&format!("collections/{id}/photos"), opt) + .await?; + + Ok(CollectionPhotos { + collection_total: headers + .get("X-Total") + .ok_or(Error::MissingHeader("X-Total"))? + .to_str() + .map_err(|_| Error::MalformedResponse)? + .parse::<usize>() + .map_err(|_| Error::MalformedResponse)?, + per_page: headers + .get("X-Per-Page") + .ok_or(Error::MissingHeader("X-Per-Page"))? + .to_str() + .map_err(|_| Error::MalformedResponse)? + .parse::<usize>() + .map_err(|_| Error::MalformedResponse)?, + photos, + }) } } @@ -88,7 +109,17 @@ mod tests { #[tokio::test] async fn collection_photos() { let client = UnsplashClient::new(&api_key()).unwrap(); - - client.collection_photos("1053828", None).await.unwrap(); + let collection = client + .collection_photos( + "1053828", + Some(CollectionPhotosOptions { + per_page: Some(5), + ..Default::default() + }), + ) + .await + .unwrap(); + + assert_eq!(collection.per_page, 5); } } diff --git a/crates/unsplash/src/model.rs b/crates/unsplash/src/model.rs @@ -26,6 +26,13 @@ pub struct CollectionPhotosOptions { } #[derive(Deserialize, Debug, Clone)] +pub struct CollectionPhotos { + pub collection_total: usize, + pub per_page: usize, + pub photos: Vec<Photo>, +} + +#[derive(Deserialize, Debug, Clone)] pub struct Photo { pub id: String, pub slug: String,