Skip to main content

openai_interface/
pagination.rs

1//! Cursor pagination helpers shared by list endpoints.
2//!
3//! > ![warn] This module is untested!
4//! > If you encounter any issues, please report them on the repository.
5//!
6//! OpenAI-compatible list endpoints return a page object with a `data`
7//! array and pagination cursor fields. The [`Page`] type models that
8//! shape generically, and [`PaginationQuery`] collects the standard query
9//! parameters so individual list requests can embed it via
10//! `Deref`-style composition or flattening.
11
12/// A single page of a paginated list response.
13///
14/// Mirrors the official list shape:
15/// `{"object": "list", "data": [...], "first_id": ..., "last_id": ...,
16/// "has_more": ...}`. Fields other than `data` are optional because some
17/// OpenAI-compatible providers omit them.
18#[derive(Debug, Clone, serde::Deserialize)]
19pub struct Page<T> {
20    /// The items on this page.
21    #[serde(default)]
22    pub data: Vec<T>,
23    /// Whether more items exist after this page.
24    #[serde(default)]
25    pub has_more: Option<bool>,
26    /// The ID of the first item on the page, for cursor pagination.
27    #[serde(default)]
28    pub first_id: Option<String>,
29    /// The ID of the last item on the page, for cursor pagination.
30    #[serde(default)]
31    pub last_id: Option<String>,
32    /// The object type (`list`), if the provider sends it.
33    #[serde(default)]
34    pub object: Option<String>,
35}
36
37/// The standard pagination query parameters accepted by most list
38/// endpoints.
39///
40/// List request structs embed this and pass its values into
41/// `query_pairs_mut` when building the URL.
42#[derive(Debug, Clone, Copy, Default)]
43pub struct PaginationQuery<'a> {
44    /// A cursor for pagination: the object ID that defines the place in
45    /// the list *before* which items are returned.
46    pub before: Option<&'a str>,
47    /// A cursor for pagination: the object ID that defines the place in
48    /// the list *after* which items are returned.
49    pub after: Option<&'a str>,
50    /// A limit on the number of objects to be returned (1-100, default
51    /// 20 for most endpoints).
52    pub limit: Option<u32>,
53    /// The sort order of the returned objects.
54    pub order: Option<PaginationOrder>,
55}
56
57/// The sort order of objects in a list response.
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
59pub enum PaginationOrder {
60    /// Ascending order (oldest first).
61    Asc,
62    /// Descending order (newest first, the default).
63    #[default]
64    Desc,
65}
66
67impl PaginationOrder {
68    /// The wire value of the order.
69    #[must_use]
70    pub fn as_str(self) -> &'static str {
71        match self {
72            PaginationOrder::Asc => "asc",
73            PaginationOrder::Desc => "desc",
74        }
75    }
76}
77
78impl<'a> PaginationQuery<'a> {
79    /// Appends the set parameters to `pairs`.
80    pub fn append_to<T: url::form_urlencoded::Target>(
81        &self,
82        pairs: &mut url::form_urlencoded::Serializer<'_, T>,
83    ) {
84        if let Some(before) = self.before {
85            pairs.append_pair("before", before);
86        }
87        if let Some(after) = self.after {
88            pairs.append_pair("after", after);
89        }
90        if let Some(limit) = self.limit {
91            pairs.append_pair("limit", &limit.to_string());
92        }
93        if let Some(order) = self.order {
94            pairs.append_pair("order", order.as_str());
95        }
96    }
97
98    /// Whether any parameter is set.
99    #[must_use]
100    pub fn any_set(&self) -> bool {
101        self.before.is_some()
102            || self.after.is_some()
103            || self.limit.is_some()
104            || self.order.is_some()
105    }
106}