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:
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,