1mod api;
5
6use crate::pages::id::PageId;
7use crate::pagination::{Pageable, Paging, PagingCursor};
8use crate::user::id::UserId;
9use chrono::{DateTime, Utc};
10use serde::ser::SerializeMap;
11use serde::{Serialize, Serializer};
12use serde_json::Number;
13
14#[derive(Serialize, Debug, Eq, PartialEq, Hash, Copy, Clone)]
15#[serde(rename_all = "snake_case")]
16pub enum SortDirection {
17 Ascending,
18 Descending,
19}
20
21#[derive(Serialize, Debug, Eq, PartialEq, Hash, Copy, Clone)]
22#[serde(rename_all = "snake_case")]
23pub enum SortTimestamp {
24 LastEditedTime,
25}
26
27#[derive(Serialize, Debug, Eq, PartialEq, Hash, Copy, Clone)]
28#[serde(rename_all = "snake_case")]
29pub enum FilterValue {
30 Page,
31 Database,
32}
33
34#[derive(Serialize, Debug, Eq, PartialEq, Hash, Copy, Clone)]
35#[serde(rename_all = "snake_case")]
36pub enum FilterProperty {
37 Object,
38}
39
40#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
41pub struct Sort {
42 timestamp: SortTimestamp,
44 direction: SortDirection,
45}
46
47#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
48pub struct Filter {
49 property: FilterProperty,
50 value: FilterValue,
51}
52
53#[derive(Serialize, Debug, Eq, PartialEq, Default)]
54pub struct SearchRequest {
55 #[serde(skip_serializing_if = "Option::is_none")]
56 query: Option<String>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 sort: Option<Sort>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 filter: Option<Filter>,
61 #[serde(flatten)]
62 paging: Option<Paging>,
63}
64
65#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
66#[serde(rename_all = "snake_case")]
67pub enum TextCondition {
68 Equals(String),
69 DoesNotEqual(String),
70 Contains(String),
71 DoesNotContain(String),
72 StartsWith(String),
73 EndsWith(String),
74 #[serde(serialize_with = "serialize_to_true")]
75 IsEmpty,
76 #[serde(serialize_with = "serialize_to_true")]
77 IsNotEmpty,
78}
79
80fn serialize_to_true<S>(serializer: S) -> Result<S::Ok, S::Error>
81where
82 S: Serializer,
83{
84 serializer.serialize_bool(true)
85}
86
87fn serialize_to_empty_object<S>(serializer: S) -> Result<S::Ok, S::Error>
88where
89 S: Serializer,
90{
91 serializer.serialize_map(Some(0))?.end()
93}
94
95#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
96#[serde(rename_all = "snake_case")]
97pub enum NumberCondition {
98 Equals(Number),
99 DoesNotEqual(Number),
100 GreaterThan(Number),
101 LessThan(Number),
102 GreaterThanOrEqualTo(Number),
103 LessThanOrEqualTo(Number),
104 #[serde(serialize_with = "serialize_to_true")]
105 IsEmpty,
106 #[serde(serialize_with = "serialize_to_true")]
107 IsNotEmpty,
108}
109
110#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
111#[serde(rename_all = "snake_case")]
112pub enum CheckboxCondition {
113 Equals(bool),
114 DoesNotEqual(bool),
115}
116
117#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
118#[serde(rename_all = "snake_case")]
119pub enum SelectCondition {
120 Equals(String),
122 DoesNotEqual(String),
124 #[serde(serialize_with = "serialize_to_true")]
126 IsEmpty,
127 #[serde(serialize_with = "serialize_to_true")]
129 IsNotEmpty,
130}
131
132#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
133#[serde(rename_all = "snake_case")]
134pub enum MultiSelectCondition {
135 Contains(String),
137 DoesNotContain(String),
139 #[serde(serialize_with = "serialize_to_true")]
141 IsEmpty,
142 #[serde(serialize_with = "serialize_to_true")]
144 IsNotEmpty,
145}
146
147#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
148#[serde(rename_all = "snake_case")]
149pub enum DateCondition {
150 Equals(DateTime<Utc>),
154 Before(DateTime<Utc>),
158 After(DateTime<Utc>),
162 OnOrBefore(DateTime<Utc>),
166 OnOrAfter(DateTime<Utc>),
170 #[serde(serialize_with = "serialize_to_true")]
172 IsEmpty,
173 #[serde(serialize_with = "serialize_to_true")]
175 IsNotEmpty,
176 #[serde(serialize_with = "serialize_to_empty_object")]
178 PastWeek,
179 #[serde(serialize_with = "serialize_to_empty_object")]
181 PastMonth,
182 #[serde(serialize_with = "serialize_to_empty_object")]
184 PastYear,
185 #[serde(serialize_with = "serialize_to_empty_object")]
187 NextWeek,
188 #[serde(serialize_with = "serialize_to_empty_object")]
190 NextMonth,
191 #[serde(serialize_with = "serialize_to_empty_object")]
193 NextYear,
194}
195
196#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
197#[serde(rename_all = "snake_case")]
198pub enum PeopleCondition {
199 Contains(UserId),
200 DoesNotContain(UserId),
202 #[serde(serialize_with = "serialize_to_true")]
204 IsEmpty,
205 #[serde(serialize_with = "serialize_to_true")]
207 IsNotEmpty,
208}
209
210#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
211#[serde(rename_all = "snake_case")]
212pub enum FilesCondition {
213 #[serde(serialize_with = "serialize_to_true")]
215 IsEmpty,
216 #[serde(serialize_with = "serialize_to_true")]
218 IsNotEmpty,
219}
220
221#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
222#[serde(rename_all = "snake_case")]
223pub enum RelationCondition {
224 Contains(PageId),
226 DoesNotContain(PageId),
228 #[serde(serialize_with = "serialize_to_true")]
230 IsEmpty,
231 #[serde(serialize_with = "serialize_to_true")]
233 IsNotEmpty,
234}
235
236#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
237#[serde(rename_all = "snake_case")]
238pub enum FormulaCondition {
239 Text(TextCondition),
242 Number(NumberCondition),
245 Checkbox(CheckboxCondition),
248 Date(DateCondition),
251}
252
253#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
254#[serde(rename_all = "snake_case")]
255pub enum PropertyCondition {
256 RichText(TextCondition),
257 Number(NumberCondition),
258 Checkbox(CheckboxCondition),
259 Select(SelectCondition),
260 MultiSelect(MultiSelectCondition),
261 Date(DateCondition),
262 People(PeopleCondition),
263 Files(FilesCondition),
264 Relation(RelationCondition),
265 Formula(FormulaCondition),
266}
267
268#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
269#[serde(untagged)]
270pub enum FilterCondition {
271 Property {
272 property: String,
273 #[serde(flatten)]
274 condition: PropertyCondition,
275 },
276 And { and: Vec<FilterCondition> },
278 Or { or: Vec<FilterCondition> },
280}
281
282#[derive(Serialize, Debug, Eq, PartialEq, Hash, Copy, Clone)]
283#[serde(rename_all = "snake_case")]
284pub enum DatabaseSortTimestamp {
285 CreatedTime,
286 LastEditedTime,
287}
288
289#[derive(Serialize, Debug, Eq, PartialEq, Clone)]
290pub struct DatabaseSort {
291 #[serde(skip_serializing_if = "Option::is_none")]
295 pub property: Option<String>,
296 #[serde(skip_serializing_if = "Option::is_none")]
298 pub timestamp: Option<DatabaseSortTimestamp>,
299 pub direction: SortDirection,
301}
302
303#[derive(Serialize, Debug, Eq, PartialEq, Default, Clone)]
304pub struct DatabaseQuery {
305 #[serde(skip_serializing_if = "Option::is_none")]
306 pub sorts: Option<Vec<DatabaseSort>>,
307 #[serde(skip_serializing_if = "Option::is_none")]
308 pub filter: Option<FilterCondition>,
309 #[serde(flatten)]
310 pub paging: Option<Paging>,
311}
312
313impl Pageable for DatabaseQuery {
314 fn start_from(self, starting_point: Option<PagingCursor>) -> Self {
315 DatabaseQuery {
316 paging: Some(Paging {
317 start_cursor: starting_point,
318 page_size: self.paging.and_then(|p| p.page_size),
319 }),
320 ..self
321 }
322 }
323}
324
325#[derive(Debug, Eq, PartialEq)]
326pub enum NotionSearch {
327 Query(String),
329 Sort {
333 timestamp: SortTimestamp,
334 direction: SortDirection,
335 },
336 Filter {
340 property: FilterProperty,
343 value: FilterValue,
346 },
347}
348
349impl NotionSearch {
350 pub fn filter_by_databases() -> Self {
351 Self::Filter {
352 property: FilterProperty::Object,
353 value: FilterValue::Database,
354 }
355 }
356}
357
358impl From<NotionSearch> for SearchRequest {
359 fn from(search: NotionSearch) -> Self {
360 match search {
361 NotionSearch::Query(query) => SearchRequest {
362 query: Some(query),
363 ..Default::default()
364 },
365 NotionSearch::Sort {
366 direction,
367 timestamp,
368 } => SearchRequest {
369 sort: Some(Sort {
370 timestamp,
371 direction,
372 }),
373 ..Default::default()
374 },
375 NotionSearch::Filter { property, value } => SearchRequest {
376 filter: Some(Filter { property, value }),
377 ..Default::default()
378 },
379 }
380 }
381}