notion_sdk/
pagination.rs

1//!
2//! # Notion Pagination
3//!
4use crate::block::Block;
5use crate::database::properties::PropertyValue;
6use crate::database::Database;
7use crate::error::ErrorResponse;
8use crate::pages::Page;
9use crate::user::User;
10use crate::Error;
11use serde::{Deserialize, Serialize};
12
13#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Clone)]
14#[serde(transparent)]
15pub struct PagingCursor(String);
16
17#[derive(Serialize, Debug, Eq, PartialEq, Default, Clone)]
18pub struct Paging {
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub start_cursor: Option<PagingCursor>,
21    #[serde(skip_serializing_if = "Option::is_none")]
22    pub page_size: Option<u8>,
23}
24
25pub trait Pageable {
26    fn start_from(self, starting_point: Option<PagingCursor>) -> Self;
27}
28
29/// <https://developers.notion.com/reference/pagination#responses>
30#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
31pub struct ListResponse<T> {
32    pub results: Vec<T>,
33    pub next_cursor: Option<PagingCursor>,
34    pub has_more: bool,
35}
36
37impl<T> ListResponse<T> {
38    pub fn results(&self) -> &[T] {
39        &self.results
40    }
41}
42impl ListResponse<Object> {
43    pub fn only_databases(self) -> ListResponse<Database> {
44        let databases = self
45            .results
46            .into_iter()
47            .filter_map(|object| match object {
48                Object::Database { database } => Some(database),
49                _ => None,
50            })
51            .collect();
52
53        ListResponse {
54            results: databases,
55            has_more: self.has_more,
56            next_cursor: self.next_cursor,
57        }
58    }
59
60    pub fn expect_databases(self) -> Result<ListResponse<Database>, Error> {
61        let databases: Result<Vec<_>, _> = self
62            .results
63            .into_iter()
64            .map(|object| match object {
65                Object::Database { database } => Ok(database),
66                response => Err(Error::UnexpectedResponse { response }),
67            })
68            .collect();
69
70        Ok(ListResponse {
71            results: databases?,
72            has_more: self.has_more,
73            next_cursor: self.next_cursor,
74        })
75    }
76
77    pub fn expect_pages(self) -> Result<ListResponse<Page>, Error> {
78        let items: Result<Vec<_>, _> = self
79            .results
80            .into_iter()
81            .map(|object| match object {
82                Object::Page { page } => Ok(page),
83                response => Err(Error::UnexpectedResponse { response }),
84            })
85            .collect();
86
87        Ok(ListResponse {
88            results: items?,
89            has_more: self.has_more,
90            next_cursor: self.next_cursor,
91        })
92    }
93
94    pub fn expect_blocks(self) -> Result<ListResponse<Block>, crate::Error> {
95        let items: Result<Vec<_>, _> = self
96            .results
97            .into_iter()
98            .map(|object| match object {
99                Object::Block { block } => Ok(block),
100                response => Err(Error::UnexpectedResponse { response }),
101            })
102            .collect();
103
104        Ok(ListResponse {
105            results: items?,
106            has_more: self.has_more,
107            next_cursor: self.next_cursor,
108        })
109    }
110}
111
112#[derive(Eq, Serialize, Deserialize, Clone, Debug, PartialEq)]
113#[serde(tag = "object")]
114#[serde(rename_all = "snake_case")]
115pub enum Object {
116    Block {
117        #[serde(flatten)]
118        block: Block,
119    },
120    Database {
121        #[serde(flatten)]
122        database: Database,
123    },
124    Page {
125        #[serde(flatten)]
126        page: Page,
127    },
128    List {
129        #[serde(flatten)]
130        list: ListResponse<Object>,
131    },
132    User {
133        #[serde(flatten)]
134        user: User,
135    },
136    PropertyItem {
137        #[serde(flatten)]
138        property_item: PropertyValue,
139    },
140    Error {
141        #[serde(flatten)]
142        error: ErrorResponse,
143    },
144}
145
146impl Object {
147    pub fn is_database(&self) -> bool {
148        matches!(self, Object::Database { .. })
149    }
150}