1use chrono::{DateTime, Utc};
2use serde::{Deserialize, Serialize};
3
4use crate::ids::{AsIdentifier, BlockId, DatabaseId, PageId};
5use crate::models::text::{RichText, TextColor};
6use crate::models::users::UserCommon;
7
8mod tests;
9
10#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
11pub struct BlockCommon {
12 pub id: BlockId,
13 pub created_time: DateTime<Utc>,
14 pub last_edited_time: DateTime<Utc>,
15 pub has_children: bool,
16 pub created_by: UserCommon,
17 pub last_edited_by: UserCommon,
18}
19
20#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
21pub struct TextAndChildren {
22 pub rich_text: Vec<RichText>,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub children: Option<Vec<Block>>,
25 pub color: TextColor,
26}
27
28#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
29pub struct Text {
30 pub rich_text: Vec<RichText>,
31}
32
33#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
34pub struct InternalFileObject {
35 url: String,
36 expiry_time: DateTime<Utc>,
37}
38
39#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
40pub struct ExternalFileObject {
41 url: String,
42}
43
44#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
45#[serde(tag = "type")]
46#[serde(rename_all = "snake_case")]
47pub enum FileOrEmojiObject {
48 Emoji { emoji: String },
49 File { file: InternalFileObject },
50 External { external: ExternalFileObject },
51}
52
53#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
54#[serde(tag = "type")]
55#[serde(rename_all = "snake_case")]
56pub enum FileObject {
57 File { file: InternalFileObject },
58 External { external: ExternalFileObject },
59}
60
61#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
62pub struct Callout {
63 pub rich_text: Vec<RichText>,
64 pub icon: FileOrEmojiObject,
65 pub color: TextColor,
66}
67
68#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
69pub struct ToDoFields {
70 pub rich_text: Vec<RichText>,
71 pub checked: bool,
72 #[serde(skip_serializing_if = "Option::is_none")]
73 pub children: Option<Vec<Block>>,
74 pub color: TextColor,
75}
76
77#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
78pub struct ChildPageFields {
79 pub title: String,
80}
81
82#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
83pub struct ChildDatabaseFields {
84 pub title: String,
85}
86
87#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
88pub struct EmbedFields {
89 pub url: String,
90}
91
92#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
93pub struct BookmarkFields {
94 pub url: String,
95 pub caption: Vec<RichText>,
96}
97
98#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
99#[serde(rename_all = "lowercase")]
100pub enum CodeLanguage {
101 Abap,
102 Arduino,
103 Bash,
104 Basic,
105 C,
106 Clojure,
107 Coffeescript,
108 #[serde(rename = "c++")]
109 CPlusPlus,
110 #[serde(rename = "c#")]
111 CSharp,
112 Css,
113 Dart,
114 Diff,
115 Docker,
116 Elixir,
117 Elm,
118 Erlang,
119 Flow,
120 Fortran,
121 #[serde(rename = "f#")]
122 FSharp,
123 Gherkin,
124 Glsl,
125 Go,
126 Graphql,
127 Groovy,
128 Haskell,
129 Html,
130 Java,
131 Javascript,
132 Json,
133 Julia,
134 Kotlin,
135 Latex,
136 Less,
137 Lisp,
138 Livescript,
139 Lua,
140 Makefile,
141 Markdown,
142 Markup,
143 Matlab,
144 Mermaid,
145 Nix,
146 #[serde(rename = "objective-c")]
147 ObjectiveC,
148 Ocaml,
149 Pascal,
150 Perl,
151 Php,
152 #[serde(rename = "plain text")]
153 PlainText,
154 Powershell,
155 Prolog,
156 Protobuf,
157 Python,
158 R,
159 Reason,
160 Ruby,
161 Rust,
162 Sass,
163 Scala,
164 Scheme,
165 Scss,
166 Shell,
167 Sql,
168 Swift,
169 Typescript,
170 #[serde(rename = "vb.net")]
171 VbNet,
172 Verilog,
173 Vhdl,
174 #[serde(rename = "visual basic")]
175 VisualBasic,
176 Webassembly,
177 Xml,
178 Yaml,
179 #[serde(rename = "java/c/c++/c#")]
180 JavaCAndCPlusPlusAndCSharp,
181}
182
183#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
184pub struct CodeFields {
185 pub rich_text: Vec<RichText>,
186 pub caption: Vec<RichText>,
187 pub language: CodeLanguage,
188}
189
190#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
191pub struct Equation {
192 pub expression: String,
193}
194
195#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
196pub struct TableOfContents {
197 pub color: TextColor,
198}
199
200#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
201pub struct ColumnListFields {
202 pub children: Vec<Block>,
203}
204
205#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
206pub struct ColumnFields {
207 pub children: Vec<Block>,
208}
209
210#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
211pub struct LinkPreviewFields {
212 pub url: String,
213}
214
215#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
216pub struct TemplateFields {
217 pub rich_text: Vec<RichText>,
218 pub children: Vec<Block>,
219}
220
221#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
222#[serde(tag = "type")]
223#[serde(rename_all = "snake_case")]
224pub enum LinkToPageFields {
225 PageId { page_id: PageId },
226 DatabaseId { database_id: DatabaseId },
227}
228
229#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
230pub struct SyncedFromObject {
231 pub block_id: BlockId,
232}
233
234#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
235pub struct SyncedBlockFields {
236 pub synced_from: Option<SyncedFromObject>,
237 pub children: Vec<Block>,
238}
239
240#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
241pub struct TableFields {
242 pub table_width: u64,
243 pub has_column_header: bool,
244 pub has_row_header: bool,
245 pub children: Vec<Block>,
246}
247
248#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
249pub struct TableRowFields {
250 pub cells: Vec<RichText>,
251}
252
253#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
254#[serde(tag = "type")]
255#[serde(rename_all = "snake_case")]
256pub enum Block {
257 Paragraph {
258 #[serde(flatten)]
259 common: BlockCommon,
260 paragraph: TextAndChildren,
261 },
262 #[serde(rename = "heading_1")]
263 Heading1 {
264 #[serde(flatten)]
265 common: BlockCommon,
266 heading_1: Text,
267 },
268 #[serde(rename = "heading_2")]
269 Heading2 {
270 #[serde(flatten)]
271 common: BlockCommon,
272 heading_2: Text,
273 },
274 #[serde(rename = "heading_3")]
275 Heading3 {
276 #[serde(flatten)]
277 common: BlockCommon,
278 heading_3: Text,
279 },
280 Callout {
281 #[serde(flatten)]
282 common: BlockCommon,
283 callout: Callout,
284 },
285 Quote {
286 #[serde(flatten)]
287 common: BlockCommon,
288 quote: TextAndChildren,
289 },
290 BulletedListItem {
291 #[serde(flatten)]
292 common: BlockCommon,
293 bulleted_list_item: TextAndChildren,
294 },
295 NumberedListItem {
296 #[serde(flatten)]
297 common: BlockCommon,
298 numbered_list_item: TextAndChildren,
299 },
300 ToDo {
301 #[serde(flatten)]
302 common: BlockCommon,
303 to_do: ToDoFields,
304 },
305 Toggle {
306 #[serde(flatten)]
307 common: BlockCommon,
308 toggle: TextAndChildren,
309 },
310 Code {
311 #[serde(flatten)]
312 common: BlockCommon,
313 code: CodeFields,
314 },
315 ChildPage {
316 #[serde(flatten)]
317 common: BlockCommon,
318 child_page: ChildPageFields,
319 },
320 ChildDatabase {
321 #[serde(flatten)]
322 common: BlockCommon,
323 child_page: ChildDatabaseFields,
324 },
325 Embed {
326 #[serde(flatten)]
327 common: BlockCommon,
328 embed: EmbedFields,
329 },
330 Image {
331 #[serde(flatten)]
332 common: BlockCommon,
333 image: FileObject,
334 },
335 Video {
336 #[serde(flatten)]
337 common: BlockCommon,
338 video: FileObject,
339 },
340 File {
341 #[serde(flatten)]
342 common: BlockCommon,
343 file: FileObject,
344 caption: Text,
345 },
346 Pdf {
347 #[serde(flatten)]
348 common: BlockCommon,
349 pdf: FileObject,
350 },
351 Bookmark {
352 #[serde(flatten)]
353 common: BlockCommon,
354 bookmark: BookmarkFields,
355 },
356 Equation {
357 #[serde(flatten)]
358 common: BlockCommon,
359 equation: Equation,
360 },
361 Divider {
362 #[serde(flatten)]
363 common: BlockCommon,
364 },
365 TableOfContents {
366 #[serde(flatten)]
367 common: BlockCommon,
368 table_of_contents: TableOfContents,
369 },
370 Breadcrumb {
371 #[serde(flatten)]
372 common: BlockCommon,
373 },
374 ColumnList {
375 #[serde(flatten)]
376 common: BlockCommon,
377 column_list: ColumnListFields,
378 },
379 Column {
380 #[serde(flatten)]
381 common: BlockCommon,
382 column: ColumnFields,
383 },
384 LinkPreview {
385 #[serde(flatten)]
386 common: BlockCommon,
387 link_preview: LinkPreviewFields,
388 },
389 Template {
390 #[serde(flatten)]
391 common: BlockCommon,
392 template: TemplateFields,
393 },
394 LinkToPage {
395 #[serde(flatten)]
396 common: BlockCommon,
397 link_to_page: LinkToPageFields,
398 },
399 Table {
400 #[serde(flatten)]
401 common: BlockCommon,
402 table: TableFields,
403 },
404 SyncedBlock {
405 #[serde(flatten)]
406 common: BlockCommon,
407 synced_block: SyncedBlockFields,
408 },
409 TableRow {
410 #[serde(flatten)]
411 common: BlockCommon,
412 table_row: TableRowFields,
413 },
414 Unsupported {
415 #[serde(flatten)]
416 common: BlockCommon,
417 },
418 #[serde(other)]
419 Unknown,
420}
421
422impl AsIdentifier<BlockId> for Block {
423 fn as_id(&self) -> &BlockId {
424 use Block::*;
425 match self {
426 Paragraph { common, .. }
427 | Heading1 { common, .. }
428 | Heading2 { common, .. }
429 | Heading3 { common, .. }
430 | Callout { common, .. }
431 | Quote { common, .. }
432 | BulletedListItem { common, .. }
433 | NumberedListItem { common, .. }
434 | ToDo { common, .. }
435 | Toggle { common, .. }
436 | Code { common, .. }
437 | ChildPage { common, .. }
438 | ChildDatabase { common, .. }
439 | Embed { common, .. }
440 | Image { common, .. }
441 | Video { common, .. }
442 | File { common, .. }
443 | Pdf { common, .. }
444 | Bookmark { common, .. }
445 | Equation { common, .. }
446 | Divider { common, .. }
447 | TableOfContents { common, .. }
448 | Breadcrumb { common, .. }
449 | ColumnList { common, .. }
450 | Column { common, .. }
451 | LinkPreview { common, .. }
452 | Template { common, .. }
453 | LinkToPage { common, .. }
454 | SyncedBlock { common, .. }
455 | Table { common, .. }
456 | TableRow { common, .. }
457 | Unsupported { common, .. } => &common.id,
458 Unknown => {
459 panic!("Trying to reference identifier for unknown block!")
460 }
461 }
462 }
463}
464
465impl Into<CreateBlock> for Block {
466 fn into(self) -> CreateBlock {
467 match self {
468 Block::Paragraph { paragraph, .. } => CreateBlock::Paragraph { paragraph },
469 Block::Heading1 { heading_1, .. } => CreateBlock::Heading1 { heading_1 },
470 Block::Heading2 { heading_2, .. } => CreateBlock::Heading2 { heading_2 },
471 Block::Heading3 { heading_3, .. } => CreateBlock::Heading3 { heading_3 },
472 Block::Callout { callout, .. } => CreateBlock::Callout { callout },
473 Block::Quote { quote, .. } => CreateBlock::Quote { quote },
474 Block::BulletedListItem {
475 bulleted_list_item, ..
476 } => CreateBlock::BulletedListItem { bulleted_list_item },
477 Block::NumberedListItem {
478 numbered_list_item, ..
479 } => CreateBlock::NumberedListItem { numbered_list_item },
480 Block::ToDo { to_do, .. } => CreateBlock::ToDo { to_do },
481 Block::Toggle { toggle, .. } => CreateBlock::Toggle { toggle },
482 Block::Code { code, .. } => CreateBlock::Code { code },
483 Block::ChildPage { child_page, .. } => CreateBlock::ChildPage { child_page },
484 Block::ChildDatabase { child_page, .. } => CreateBlock::ChildDatabase { child_page },
485 Block::Embed { embed, .. } => CreateBlock::Embed { embed },
486 Block::Image { image, .. } => CreateBlock::Image { image },
487 Block::Video { video, .. } => CreateBlock::Video { video },
488 Block::File { file, caption, .. } => CreateBlock::File { file, caption },
489 Block::Pdf { pdf, .. } => CreateBlock::Pdf { pdf },
490 Block::Bookmark { bookmark, .. } => CreateBlock::Bookmark { bookmark },
491 Block::Equation { equation, .. } => CreateBlock::Equation { equation },
492 Block::Divider { .. } => CreateBlock::Divider {},
493 Block::TableOfContents {
494 table_of_contents, ..
495 } => CreateBlock::TableOfContents { table_of_contents },
496 Block::Breadcrumb { .. } => CreateBlock::Breadcrumb {},
497 Block::ColumnList { column_list, .. } => CreateBlock::ColumnList { column_list },
498 Block::Column { column, .. } => CreateBlock::Column { column },
499
500 Block::LinkPreview { link_preview, .. } => CreateBlock::LinkPreview { link_preview },
501 Block::Template { template, .. } => CreateBlock::Template { template },
502 Block::LinkToPage { link_to_page, .. } => CreateBlock::LinkToPage { link_to_page },
503 Block::Table { table, .. } => CreateBlock::Table { table },
504 Block::SyncedBlock { synced_block, .. } => CreateBlock::SyncedBlock { synced_block },
505 Block::TableRow { table_row, .. } => CreateBlock::TableRow { table_row },
506 Block::Unsupported { .. } => CreateBlock::Unsupported,
507 Block::Unknown => CreateBlock::Unknown,
508 }
509 }
510}
511
512#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
513#[serde(tag = "type")]
514#[serde(rename_all = "snake_case")]
515pub enum CreateBlock {
516 Paragraph {
517 paragraph: TextAndChildren,
518 },
519 #[serde(rename = "heading_1")]
520 Heading1 {
521 heading_1: Text,
522 },
523 #[serde(rename = "heading_2")]
524 Heading2 {
525 heading_2: Text,
526 },
527 #[serde(rename = "heading_3")]
528 Heading3 {
529 heading_3: Text,
530 },
531 Callout {
532 callout: Callout,
533 },
534 Quote {
535 quote: TextAndChildren,
536 },
537 BulletedListItem {
538 bulleted_list_item: TextAndChildren,
539 },
540 NumberedListItem {
541 numbered_list_item: TextAndChildren,
542 },
543 ToDo {
544 to_do: ToDoFields,
545 },
546 Toggle {
547 toggle: TextAndChildren,
548 },
549 Code {
550 code: CodeFields,
551 },
552 ChildPage {
553 child_page: ChildPageFields,
554 },
555 ChildDatabase {
556 child_page: ChildDatabaseFields,
557 },
558 Embed {
559 embed: EmbedFields,
560 },
561 Image {
562 image: FileObject,
563 },
564 Video {
565 video: FileObject,
566 },
567 File {
568 file: FileObject,
569 caption: Text,
570 },
571 Pdf {
572 pdf: FileObject,
573 },
574 Bookmark {
575 bookmark: BookmarkFields,
576 },
577 Equation {
578 equation: Equation,
579 },
580 Divider,
581 TableOfContents {
582 table_of_contents: TableOfContents,
583 },
584 Breadcrumb,
585 ColumnList {
586 column_list: ColumnListFields,
587 },
588 Column {
589 column: ColumnFields,
590 },
591 LinkPreview {
592 link_preview: LinkPreviewFields,
593 },
594 Template {
595 template: TemplateFields,
596 },
597 LinkToPage {
598 link_to_page: LinkToPageFields,
599 },
600 Table {
601 table: TableFields,
602 },
603 SyncedBlock {
604 synced_block: SyncedBlockFields,
605 },
606 TableRow {
607 table_row: TableRowFields,
608 },
609 Unsupported,
610 #[serde(other)]
611 Unknown,
612}