1use crate::MastodonClient;
2use crate::error::Result;
3use serde::de::DeserializeOwned;
4
5pub struct PagedRequest<'a, T> {
10 client: &'a MastodonClient,
11 next_url: Option<String>,
12 _marker: std::marker::PhantomData<T>,
13}
14
15impl<'a, T: DeserializeOwned> PagedRequest<'a, T> {
16 pub fn new(client: &'a MastodonClient, initial_url: String) -> Self {
18 Self {
19 client,
20 next_url: Some(initial_url),
21 _marker: std::marker::PhantomData,
22 }
23 }
24
25 pub async fn next_page(&mut self) -> Result<Option<Vec<T>>> {
30 let url = match &self.next_url {
31 Some(u) => u,
32 None => return Ok(None),
33 };
34
35 let response = self.client.http_client().get(url);
36 let builder = response;
37
38 let mut req_builder = builder;
39 if let Some(token) = self.client.access_token() {
40 req_builder = req_builder.bearer_auth(token);
41 }
42
43 let resp = req_builder.send().await?;
44
45 if !resp.status().is_success() {
46 return Err(crate::error::MastodonError::ApiError {
47 status: resp.status(),
48 message: resp.text().await.unwrap_or_default(),
49 });
50 }
51
52 self.next_url = parse_link_header(resp.headers().get("Link"));
54
55 Ok(Some(resp.json().await?))
56 }
57}
58
59fn parse_link_header(header: Option<&reqwest::header::HeaderValue>) -> Option<String> {
61 let header_str = header?.to_str().ok()?;
62 for part in header_str.split(',') {
64 if part.contains("rel=\"next\"") {
65 let start = part.find('<')? + 1;
66 let end = part.find('>')?;
67 return Some(part[start..end].to_string());
68 }
69 }
70 None
71}