notion_api_client/models/
mod.rs

1pub mod block;
2pub mod error;
3pub mod paging;
4pub mod properties;
5pub mod search;
6#[cfg(test)]
7mod tests;
8pub mod text;
9pub mod users;
10
11use crate::models::properties::{PropertyConfiguration, PropertyValue};
12use crate::models::text::RichText;
13use crate::Error;
14use serde::{Deserialize, Serialize};
15use std::collections::HashMap;
16
17use crate::ids::{AsIdentifier, DatabaseId, PageId};
18use crate::models::block::{Block, CreateBlock};
19use crate::models::error::ErrorResponse;
20use crate::models::paging::PagingCursor;
21use crate::models::users::User;
22pub use chrono::{DateTime, Utc};
23pub use serde_json::value::Number;
24
25#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
26#[serde(rename_all = "snake_case")]
27enum ObjectType {
28    Database,
29    List,
30}
31
32/// Represents a Notion Database
33/// See <https://developers.notion.com/reference/database>
34#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
35pub struct Database {
36    /// Unique identifier for the database.
37    pub id: DatabaseId,
38    /// Date and time when this database was created.
39    pub created_time: DateTime<Utc>,
40    /// Date and time when this database was updated.
41    pub last_edited_time: DateTime<Utc>,
42    /// Name of the database as it appears in Notion.
43    pub title: Vec<RichText>,
44    /// Schema of properties for the database as they appear in Notion.
45    //
46    // key string
47    // The name of the property as it appears in Notion.
48    //
49    // value object
50    // A Property object.
51    pub properties: HashMap<String, PropertyConfiguration>,
52}
53
54impl AsIdentifier<DatabaseId> for Database {
55    fn as_id(&self) -> &DatabaseId {
56        &self.id
57    }
58}
59
60impl Database {
61    pub fn title_plain_text(&self) -> String {
62        self.title
63            .iter()
64            .flat_map(|rich_text| rich_text.plain_text().chars())
65            .collect()
66    }
67}
68
69/// <https://developers.notion.com/reference/pagination#responses>
70#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
71pub struct ListResponse<T> {
72    pub results: Vec<T>,
73    pub next_cursor: Option<PagingCursor>,
74    pub has_more: bool,
75}
76
77impl<T> ListResponse<T> {
78    pub fn results(&self) -> &[T] {
79        &self.results
80    }
81}
82
83impl ListResponse<Object> {
84    pub fn only_databases(self) -> ListResponse<Database> {
85        let databases = self
86            .results
87            .into_iter()
88            .filter_map(|object| match object {
89                Object::Database { database } => Some(database),
90                _ => None,
91            })
92            .collect();
93
94        ListResponse {
95            results: databases,
96            has_more: self.has_more,
97            next_cursor: self.next_cursor,
98        }
99    }
100
101    pub(crate) fn expect_databases(self) -> Result<ListResponse<Database>, crate::Error> {
102        let databases: Result<Vec<_>, _> = self
103            .results
104            .into_iter()
105            .map(|object| match object {
106                Object::Database { database } => Ok(database),
107                response => Err(Error::UnexpectedResponse { response }),
108            })
109            .collect();
110
111        Ok(ListResponse {
112            results: databases?,
113            has_more: self.has_more,
114            next_cursor: self.next_cursor,
115        })
116    }
117
118    pub(crate) fn expect_pages(self) -> Result<ListResponse<Page>, crate::Error> {
119        let items: Result<Vec<_>, _> = self
120            .results
121            .into_iter()
122            .map(|object| match object {
123                Object::Page { page } => Ok(page),
124                response => Err(Error::UnexpectedResponse { response }),
125            })
126            .collect();
127
128        Ok(ListResponse {
129            results: items?,
130            has_more: self.has_more,
131            next_cursor: self.next_cursor,
132        })
133    }
134
135    pub(crate) fn expect_blocks(self) -> Result<ListResponse<Block>, crate::Error> {
136        let items: Result<Vec<_>, _> = self
137            .results
138            .into_iter()
139            .map(|object| match object {
140                Object::Block { block } => Ok(block),
141                response => Err(Error::UnexpectedResponse { response }),
142            })
143            .collect();
144
145        Ok(ListResponse {
146            results: items?,
147            has_more: self.has_more,
148            next_cursor: self.next_cursor,
149        })
150    }
151}
152
153#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
154#[serde(tag = "type")]
155#[serde(rename_all = "snake_case")]
156pub enum Parent {
157    #[serde(rename = "database_id")]
158    Database {
159        database_id: DatabaseId,
160    },
161    #[serde(rename = "page_id")]
162    Page {
163        page_id: PageId,
164    },
165    Workspace,
166}
167
168#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
169pub struct Properties {
170    #[serde(flatten)]
171    pub properties: HashMap<String, PropertyValue>,
172}
173
174impl Properties {
175    pub fn title(&self) -> Option<String> {
176        self.properties.values().find_map(|p| match p {
177            PropertyValue::Title { title, .. } => {
178                Some(title.iter().map(|t| t.plain_text()).collect())
179            }
180            _ => None,
181        })
182    }
183}
184
185#[derive(Serialize, Debug, Eq, PartialEq)]
186pub struct PageCreateRequest {
187    pub parent: Parent,
188    pub properties: Properties,
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub children: Option<Vec<CreateBlock>>,
191}
192
193#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
194pub struct Page {
195    pub id: PageId,
196    /// Date and time when this page was created.
197    pub created_time: DateTime<Utc>,
198    /// Date and time when this page was updated.
199    pub last_edited_time: DateTime<Utc>,
200    /// The archived status of the page.
201    pub archived: bool,
202    pub properties: Properties,
203    pub parent: Parent,
204}
205
206impl Page {
207    pub fn title(&self) -> Option<String> {
208        self.properties.title()
209    }
210}
211
212impl AsIdentifier<PageId> for Page {
213    fn as_id(&self) -> &PageId {
214        &self.id
215    }
216}
217
218#[derive(Eq, Serialize, Deserialize, Clone, Debug, PartialEq)]
219#[serde(tag = "object")]
220#[serde(rename_all = "snake_case")]
221pub enum Object {
222    Block {
223        #[serde(flatten)]
224        block: Block,
225    },
226    Database {
227        #[serde(flatten)]
228        database: Database,
229    },
230    Page {
231        #[serde(flatten)]
232        page: Page,
233    },
234    List {
235        #[serde(flatten)]
236        list: ListResponse<Object>,
237    },
238    User {
239        #[serde(flatten)]
240        user: User,
241    },
242    Error {
243        #[serde(flatten)]
244        error: ErrorResponse,
245    },
246}
247
248impl Object {
249    pub fn is_database(&self) -> bool {
250        matches!(self, Object::Database { .. })
251    }
252}