aboutsummaryrefslogtreecommitdiff
path: root/src/models
diff options
context:
space:
mode:
Diffstat (limited to 'src/models')
-rw-r--r--src/models/mod.rs700
-rw-r--r--src/models/properties.rs1
-rw-r--r--src/models/tests.rs449
-rw-r--r--src/models/tests/callout.json43
-rw-r--r--src/models/tests/emoji_object.json4
-rw-r--r--src/models/tests/external_file_object.json6
-rw-r--r--src/models/tests/file_object.json7
-rw-r--r--src/models/tests/heading_1.json175
-rw-r--r--src/models/tests/rich_text_mention_date.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_end.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_end_and_time.json21
-rw-r--r--src/models/tests/rich_text_mention_date_with_time.json21
-rw-r--r--src/models/tests/rich_text_mention_user_person.json26
-rw-r--r--src/models/tests/rich_text_text.json19
-rw-r--r--src/models/text.rs31
15 files changed, 1545 insertions, 0 deletions
diff --git a/src/models/mod.rs b/src/models/mod.rs
new file mode 100644
index 0000000..175f3c5
--- /dev/null
+++ b/src/models/mod.rs
@@ -0,0 +1,700 @@
+pub mod error;
+pub mod paging;
+pub mod properties;
+pub mod search;
+#[cfg(test)]
+mod tests;
+pub mod text;
+pub mod users;
+
+use crate::models::properties::{PropertyConfiguration, PropertyValue};
+use crate::models::text::{RichText, TextColor};
+use crate::Error;
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+
+use crate::ids::{AsIdentifier, BlockId, DatabaseId, PageId};
+use crate::models::error::ErrorResponse;
+use crate::models::paging::PagingCursor;
+use crate::models::users::{User, UserCommon};
+pub use chrono::{DateTime, Utc};
+pub use serde_json::value::Number;
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone)]
+#[serde(rename_all = "snake_case")]
+enum ObjectType {
+ Database,
+ List,
+}
+
+/// Represents a Notion Database
+/// See <https://developers.notion.com/reference/database>
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Database {
+ /// Unique identifier for the database.
+ pub id: DatabaseId,
+ /// Date and time when this database was created.
+ pub created_time: DateTime<Utc>,
+ /// Date and time when this database was updated.
+ pub last_edited_time: DateTime<Utc>,
+ /// Name of the database as it appears in Notion.
+ pub title: Vec<RichText>,
+ /// Schema of properties for the database as they appear in Notion.
+ //
+ // key string
+ // The name of the property as it appears in Notion.
+ //
+ // value object
+ // A Property object.
+ pub properties: HashMap<String, PropertyConfiguration>,
+}
+
+impl AsIdentifier<DatabaseId> for Database {
+ fn as_id(&self) -> &DatabaseId {
+ &self.id
+ }
+}
+
+impl Database {
+ pub fn title_plain_text(&self) -> String {
+ self.title
+ .iter()
+ .flat_map(|rich_text| rich_text.plain_text().chars())
+ .collect()
+ }
+}
+
+/// <https://developers.notion.com/reference/pagination#responses>
+#[derive(Serialize, Deserialize, Eq, PartialEq, Debug, Clone)]
+pub struct ListResponse<T> {
+ pub results: Vec<T>,
+ pub next_cursor: Option<PagingCursor>,
+ pub has_more: bool,
+}
+
+impl<T> ListResponse<T> {
+ pub fn results(&self) -> &[T] {
+ &self.results
+ }
+}
+
+impl ListResponse<Object> {
+ pub fn only_databases(self) -> ListResponse<Database> {
+ let databases = self
+ .results
+ .into_iter()
+ .filter_map(|object| match object {
+ Object::Database { database } => Some(database),
+ _ => None,
+ })
+ .collect();
+
+ ListResponse {
+ results: databases,
+ has_more: self.has_more,
+ next_cursor: self.next_cursor,
+ }
+ }
+
+ pub(crate) fn expect_databases(self) -> Result<ListResponse<Database>, crate::Error> {
+ let databases: Result<Vec<_>, _> = self
+ .results
+ .into_iter()
+ .map(|object| match object {
+ Object::Database { database } => Ok(database),
+ response => Err(Error::UnexpectedResponse { response }),
+ })
+ .collect();
+
+ Ok(ListResponse {
+ results: databases?,
+ has_more: self.has_more,
+ next_cursor: self.next_cursor,
+ })
+ }
+
+ pub(crate) fn expect_pages(self) -> Result<ListResponse<Page>, crate::Error> {
+ let items: Result<Vec<_>, _> = self
+ .results
+ .into_iter()
+ .map(|object| match object {
+ Object::Page { page } => Ok(page),
+ response => Err(Error::UnexpectedResponse { response }),
+ })
+ .collect();
+
+ Ok(ListResponse {
+ results: items?,
+ has_more: self.has_more,
+ next_cursor: self.next_cursor,
+ })
+ }
+
+ pub(crate) fn expect_blocks(self) -> Result<ListResponse<Block>, crate::Error> {
+ let items: Result<Vec<_>, _> = self
+ .results
+ .into_iter()
+ .map(|object| match object {
+ Object::Block { block } => Ok(block),
+ response => Err(Error::UnexpectedResponse { response }),
+ })
+ .collect();
+
+ Ok(ListResponse {
+ results: items?,
+ has_more: self.has_more,
+ next_cursor: self.next_cursor,
+ })
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum Parent {
+ #[serde(rename = "database_id")]
+ Database {
+ database_id: DatabaseId,
+ },
+ #[serde(rename = "page_id")]
+ Page {
+ page_id: PageId,
+ },
+ Workspace,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Properties {
+ #[serde(flatten)]
+ pub properties: HashMap<String, PropertyValue>,
+}
+
+impl Properties {
+ pub fn title(&self) -> Option<String> {
+ self.properties.values().find_map(|p| match p {
+ PropertyValue::Title { title, .. } => {
+ Some(title.into_iter().map(|t| t.plain_text()).collect())
+ }
+ _ => None,
+ })
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Page {
+ pub id: PageId,
+ /// Date and time when this page was created.
+ pub created_time: DateTime<Utc>,
+ /// Date and time when this page was updated.
+ pub last_edited_time: DateTime<Utc>,
+ /// The archived status of the page.
+ pub archived: bool,
+ pub properties: Properties,
+ pub parent: Parent,
+}
+
+impl Page {
+ pub fn title(&self) -> Option<String> {
+ self.properties.title()
+ }
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct BlockCommon {
+ pub id: BlockId,
+ pub created_time: DateTime<Utc>,
+ pub last_edited_time: DateTime<Utc>,
+ pub has_children: bool,
+ pub created_by: UserCommon,
+ pub last_edited_by: UserCommon,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TextAndChildren {
+ pub rich_text: Vec<RichText>,
+ pub children: Option<Vec<Block>>,
+ pub color: TextColor,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Text {
+ pub rich_text: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct InternalFileObject {
+ url: String,
+ expiry_time: DateTime<Utc>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ExternalFileObject {
+ url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum FileOrEmojiObject {
+ Emoji { emoji: String },
+ File { file: InternalFileObject },
+ External { external: ExternalFileObject },
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum FileObject {
+ File { file: InternalFileObject },
+ External { external: ExternalFileObject },
+}
+
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Callout {
+ pub rich_text: Vec<RichText>,
+ pub icon: FileOrEmojiObject,
+ pub color: TextColor,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ToDoFields {
+ pub rich_text: Vec<RichText>,
+ pub checked: bool,
+ pub children: Option<Vec<Block>>,
+ pub color: TextColor,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ChildPageFields {
+ pub title: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ChildDatabaseFields {
+ pub title: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct EmbedFields {
+ pub url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct BookmarkFields {
+ pub url: String,
+ pub caption: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(rename_all = "lowercase")]
+pub enum CodeLanguage {
+ Abap,
+ Arduino,
+ Bash,
+ Basic,
+ C,
+ Clojure,
+ Coffeescript,
+ #[serde(rename = "c++")]
+ CPlusPlus,
+ #[serde(rename = "c#")]
+ CSharp,
+ Css,
+ Dart,
+ Diff,
+ Docker,
+ Elixir,
+ Elm,
+ Erlang,
+ Flow,
+ Fortran,
+ #[serde(rename = "f#")]
+ FSharp,
+ Gherkin,
+ Glsl,
+ Go,
+ Graphql,
+ Groovy,
+ Haskell,
+ Html,
+ Java,
+ Javascript,
+ Json,
+ Julia,
+ Kotlin,
+ Latex,
+ Less,
+ Lisp,
+ Livescript,
+ Lua,
+ Makefile,
+ Markdown,
+ Markup,
+ Matlab,
+ Mermaid,
+ Nix,
+ #[serde(rename = "objective-c")]
+ ObjectiveC,
+ Ocaml,
+ Pascal,
+ Perl,
+ Php,
+ #[serde(rename = "plain text")]
+ PlainText,
+ Powershell,
+ Prolog,
+ Protobuf,
+ Python,
+ R,
+ Reason,
+ Ruby,
+ Rust,
+ Sass,
+ Scala,
+ Scheme,
+ Scss,
+ Shell,
+ Sql,
+ Swift,
+ Typescript,
+ #[serde(rename = "vb.net")]
+ VbNet,
+ Verilog,
+ Vhdl,
+ #[serde(rename = "visual basic")]
+ VisualBasic,
+ Webassembly,
+ Xml,
+ Yaml,
+ #[serde(rename = "java/c/c++/c#")]
+ JavaCAndCPlusPlusAndCSharp,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct CodeFields {
+ pub rich_text: Vec<RichText>,
+ pub caption: Vec<RichText>,
+ pub language: CodeLanguage,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct Equation {
+ pub expression: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableOfContents {
+ pub color: TextColor,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ColumnListFields {
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct ColumnFields {
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct LinkPreviewFields {
+ pub url: String,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TemplateFields {
+ pub rich_text: Vec<RichText>,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum LinkToPageFields {
+ PageId {
+ page_id: PageId
+ },
+ DatabaseId {
+ database_id: DatabaseId
+ },
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct SyncedFromObject {
+ pub block_id: BlockId,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct SyncedBlockFields {
+ pub synced_from: Option<SyncedFromObject>,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableFields {
+ pub table_width: u64,
+ pub has_column_header: bool,
+ pub has_row_header: bool,
+ pub children: Vec<Block>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+pub struct TableRowFields {
+ pub cells: Vec<RichText>,
+}
+
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum Block {
+ Paragraph {
+ #[serde(flatten)]
+ common: BlockCommon,
+ paragraph: TextAndChildren,
+ },
+ #[serde(rename = "heading_1")]
+ Heading1 {
+ #[serde(flatten)]
+ common: BlockCommon,
+ heading_1: Text,
+ },
+ #[serde(rename = "heading_2")]
+ Heading2 {
+ #[serde(flatten)]
+ common: BlockCommon,
+ heading_2: Text,
+ },
+ #[serde(rename = "heading_3")]
+ Heading3 {
+ #[serde(flatten)]
+ common: BlockCommon,
+ heading_3: Text,
+ },
+ Callout {
+ #[serde(flatten)]
+ common: BlockCommon,
+ callout: Callout,
+ },
+ Quote {
+ #[serde(flatten)]
+ common: BlockCommon,
+ quote: TextAndChildren,
+ },
+ BulletedListItem {
+ #[serde(flatten)]
+ common: BlockCommon,
+ bulleted_list_item: TextAndChildren,
+ },
+ NumberedListItem {
+ #[serde(flatten)]
+ common: BlockCommon,
+ numbered_list_item: TextAndChildren,
+ },
+ ToDo {
+ #[serde(flatten)]
+ common: BlockCommon,
+ to_do: ToDoFields,
+ },
+ Toggle {
+ #[serde(flatten)]
+ common: BlockCommon,
+ toggle: TextAndChildren,
+ },
+ Code {
+ #[serde(flatten)]
+ common: BlockCommon,
+ code: CodeFields,
+ },
+ ChildPage {
+ #[serde(flatten)]
+ common: BlockCommon,
+ child_page: ChildPageFields,
+ },
+ ChildDatabase {
+ #[serde(flatten)]
+ common: BlockCommon,
+ child_page: ChildDatabaseFields,
+ },
+ Embed {
+ #[serde(flatten)]
+ common: BlockCommon,
+ embed: EmbedFields,
+ },
+ Image {
+ #[serde(flatten)]
+ common: BlockCommon,
+ image: FileObject,
+ },
+ Video {
+ #[serde(flatten)]
+ common: BlockCommon,
+ video: FileObject,
+ },
+ File {
+ #[serde(flatten)]
+ common: BlockCommon,
+ file: FileObject,
+ caption: Text,
+ },
+ Pdf {
+ #[serde(flatten)]
+ common: BlockCommon,
+ pdf: FileObject,
+ },
+ Bookmark {
+ #[serde(flatten)]
+ common: BlockCommon,
+ bookmark: BookmarkFields,
+ },
+ Equation {
+ #[serde(flatten)]
+ common: BlockCommon,
+ equation: Equation,
+ },
+ Divider {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
+ TableOfContents {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table_of_contents: TableOfContents,
+ },
+ Breadcrumb {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
+ ColumnList {
+ #[serde(flatten)]
+ common: BlockCommon,
+ column_list: ColumnListFields,
+ },
+ Column {
+ #[serde(flatten)]
+ common: BlockCommon,
+ column: ColumnFields,
+ },
+ LinkPreview {
+ #[serde(flatten)]
+ common: BlockCommon,
+ link_preview: LinkPreviewFields,
+ },
+ Template {
+ #[serde(flatten)]
+ common: BlockCommon,
+ template: TemplateFields,
+ },
+ LinkToPage {
+ #[serde(flatten)]
+ common: BlockCommon,
+ link_to_page: LinkToPageFields,
+ },
+ Table {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table: TableFields,
+ },
+ SyncedBlock {
+ #[serde(flatten)]
+ common: BlockCommon,
+ synced_block: SyncedBlockFields,
+ },
+ TableRow {
+ #[serde(flatten)]
+ common: BlockCommon,
+ table_row: TableRowFields,
+ },
+ Unsupported {
+ #[serde(flatten)]
+ common: BlockCommon,
+ },
+ #[serde(other)]
+ Unknown,
+}
+
+impl AsIdentifier<BlockId> for Block {
+ fn as_id(&self) -> &BlockId {
+ use Block::*;
+ match self {
+ Paragraph { common, .. }
+ | Heading1 { common, .. }
+ | Heading2 { common, .. }
+ | Heading3 { common, .. }
+ | Callout { common, .. }
+ | Quote { common, .. }
+ | BulletedListItem { common, .. }
+ | NumberedListItem { common, .. }
+ | ToDo { common, .. }
+ | Toggle { common, .. }
+ | Code { common, .. }
+ | ChildPage { common, .. }
+ | ChildDatabase { common, .. }
+ | Embed { common, .. }
+ | Image { common, .. }
+ | Video { common, .. }
+ | File { common, .. }
+ | Pdf { common, .. }
+ | Bookmark { common, .. }
+ | Equation { common, .. }
+ | Divider { common, .. }
+ | TableOfContents { common, .. }
+ | Breadcrumb { common, .. }
+ | ColumnList { common, .. }
+ | Column { common, .. }
+ | LinkPreview { common, .. }
+ | Template { common, .. }
+ | LinkToPage { common, .. }
+ | SyncedBlock { common, .. }
+ | Table { common, .. }
+ | TableRow { common, .. }
+ | Unsupported { common, .. } => { &common.id }
+ Unknown => {
+ panic!("Trying to reference identifier for unknown block!")
+ }
+ }
+ }
+}
+
+impl AsIdentifier<PageId> for Page {
+ fn as_id(&self) -> &PageId {
+ &self.id
+ }
+}
+
+#[derive(Eq, Serialize, Deserialize, Clone, Debug, PartialEq)]
+#[serde(tag = "object")]
+#[serde(rename_all = "snake_case")]
+pub enum Object {
+ Block {
+ #[serde(flatten)]
+ block: Block,
+ },
+ Database {
+ #[serde(flatten)]
+ database: Database,
+ },
+ Page {
+ #[serde(flatten)]
+ page: Page,
+ },
+ List {
+ #[serde(flatten)]
+ list: ListResponse<Object>,
+ },
+ User {
+ #[serde(flatten)]
+ user: User,
+ },
+ Error {
+ #[serde(flatten)]
+ error: ErrorResponse,
+ },
+}
+
+impl Object {
+ pub fn is_database(&self) -> bool {
+ matches!(self, Object::Database { .. })
+ }
+}
diff --git a/src/models/properties.rs b/src/models/properties.rs
index 0a2ed97..948a0a0 100644
--- a/src/models/properties.rs
+++ b/src/models/properties.rs
@@ -206,6 +206,7 @@ pub enum DateOrDateTime {
pub struct DateValue {
pub start: DateOrDateTime,
pub end: Option<DateOrDateTime>,
+ pub time_zone: Option<String>
}
/// Formula property value objects represent the result of evaluating a formula
diff --git a/src/models/tests.rs b/src/models/tests.rs
new file mode 100644
index 0000000..fa58d54
--- /dev/null
+++ b/src/models/tests.rs
@@ -0,0 +1,449 @@
+use std::str::FromStr;
+use chrono::{DateTime, NaiveDate};
+use crate::{Block, BlockId, models};
+use crate::ids::UserId;
+use crate::models::text::{Annotations, Link, MentionObject, RichText, RichTextCommon, Text, TextColor};
+use crate::models::{BlockCommon, Callout, ExternalFileObject, InternalFileObject, FileOrEmojiObject, ListResponse, Object, Page};
+use crate::models::properties::{DateOrDateTime, DateValue};
+use crate::models::users::{Person, User, UserCommon};
+
+#[test]
+fn deserialize_page() {
+ let _page: Page = serde_json::from_str(include_str!("tests/page.json")).unwrap();
+}
+
+#[test]
+fn deserialize_query_result() {
+ let _page: ListResponse<Page> =
+ serde_json::from_str(include_str!("tests/query_result.json")).unwrap();
+}
+
+#[test]
+fn deserialize_number_format() {
+ let _search_results: ListResponse<Object> =
+ serde_json::from_str(include_str!("tests/issue_15.json")).unwrap();
+}
+
+#[test]
+fn rich_text() {
+ let rich_text_text: RichText = serde_json::from_str(include_str!("tests/rich_text_text.json")).unwrap();
+ assert_eq!(rich_text_text, RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Rich".to_string(),
+ href: Some("https://github.com/jakeswenson/notion".to_string()),
+ annotations: Some(Annotations {
+ bold: Some(true),
+ code: Some(true),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(true),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "Rich".to_string(),
+ link: Some(Link {
+ url: "https://github.com/jakeswenson/notion".to_string()
+ }),
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_user_person() {
+ let rich_text_mention_user_person: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_user_person.json")).unwrap();
+ assert_eq!(rich_text_mention_user_person, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "@John Doe".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::User {
+ user: User::Person {
+ common: UserCommon {
+ id: UserId::from_str("1118608e-35e8-4fa3-aef7-a4ced85ce8e0").unwrap(),
+ name: Some("John Doe".to_string()),
+ avatar_url: Some("https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg".to_string()),
+ },
+ person: Person { email: "john.doe@gmail.com".to_string() },
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date() {
+ let rich_text_mention_date: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date.json")).unwrap();
+ assert_eq!(rich_text_mention_date, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-04-16 → ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::Date(NaiveDate::from_str("2022-04-16").unwrap()),
+ end: None,
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_time() {
+ let rich_text_mention_date_with_time: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_time.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_time, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-05-14T09:00:00.000-04:00 → ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::DateTime(DateTime::from_str("2022-05-14T09:00:00.000-04:00").unwrap()),
+ end: None,
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_end() {
+ let rich_text_mention_date_with_end: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_end.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_end, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-05-12 → 2022-05-13".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::Date(NaiveDate::from_str("2022-05-12").unwrap()),
+ end: Some(DateOrDateTime::Date(NaiveDate::from_str("2022-05-13").unwrap())),
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn rich_text_mention_date_with_end_and_time() {
+ let rich_text_mention_date_with_end_and_time: RichText = serde_json::from_str(include_str!("tests/rich_text_mention_date_with_end_and_time.json")).unwrap();
+ assert_eq!(rich_text_mention_date_with_end_and_time, RichText::Mention {
+ rich_text: RichTextCommon {
+ plain_text: "2022-04-16T12:00:00.000-04:00 → 2022-04-16T12:00:00.000-04:00".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ mention: MentionObject::Date {
+ date: DateValue {
+ start: DateOrDateTime::DateTime(DateTime::from_str("2022-04-16T12:00:00.000-04:00").unwrap()),
+ end: Some(DateOrDateTime::DateTime(DateTime::from_str("2022-04-16T12:00:00.000-04:00").unwrap())),
+ time_zone: None,
+ }
+ },
+ })
+}
+
+#[test]
+fn heading_1() {
+ let heading_1: Block = serde_json::from_str(include_str!("tests/heading_1.json")).unwrap();
+ assert_eq!(heading_1, Block::Heading1 {
+ common: BlockCommon {
+ id: BlockId::from_str("9e891834-6a03-475c-a2b8-421e17f0f3aa").unwrap(),
+ created_time: DateTime::from_str("2022-05-12T21:15:00.000Z").unwrap(),
+ last_edited_time: DateTime::from_str("2022-05-12T22:10:00.000Z").unwrap(),
+ has_children: false,
+ created_by: UserCommon {
+ id: UserId::from_str("6419f912-5293-4ea8-b2c8-9c3ce44f90e3").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ last_edited_by: UserCommon {
+ id: UserId::from_str("6419f912-5293-4ea8-b2c8-9c3ce44f90e3").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ },
+ heading_1: models::Text {
+ rich_text: vec![
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "This".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(true),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "This".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "is".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "is".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "a".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(false),
+ underline: Some(true),
+ }),
+ },
+ text: Text {
+ content: "a".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Heading".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(true),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "Heading".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: " ".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: " ".to_string(),
+ link: None,
+ },
+ },
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "1".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(true),
+ underline: Some(false),
+ }),
+ },
+ text: Text {
+ content: "1".to_string(),
+ link: None,
+ },
+ },
+ ]
+ },
+ })
+}
+
+#[test]
+fn emoji_object() {
+ let emoji_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/emoji_object.json")).unwrap();
+ assert_eq!(emoji_object, FileOrEmojiObject::Emoji {
+ emoji: "💡".to_string()
+ })
+}
+
+#[test]
+fn file_object() {
+ let file_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/file_object.json")).unwrap();
+ assert_eq!(file_object, FileOrEmojiObject::File {
+ file: InternalFileObject {
+ url: "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2703e742-ace5-428c-a74d-1c587ceddc32/DiRT_Rally.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220513%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220513T201035Z&X-Amz-Expires=3600&X-Amz-Signature=714b49bde0b499fb8f3aae1a88a8cbd374f2b09c1d128e91cac49e85ce0e00fb&X-Amz-SignedHeaders=host&x-id=GetObject".to_string(),
+ expiry_time: DateTime::from_str("2022-05-13T21:10:35.817Z").unwrap(),
+ }
+ })
+}
+
+#[test]
+fn external_file_object() {
+ let external_file_object: FileOrEmojiObject = serde_json::from_str(include_str!("tests/external_file_object.json")).unwrap();
+ assert_eq!(external_file_object, FileOrEmojiObject::External {
+ external: ExternalFileObject {
+ url: "https://nerdist.com/wp-content/uploads/2020/07/maxresdefault.jpg".to_string(),
+ }
+ })
+}
+
+#[test]
+fn callout() {
+ let callout: Object = serde_json::from_str(include_str!("tests/callout.json")).unwrap();
+ assert_eq!(callout, Object::Block {
+ block: Block::Callout {
+ common: BlockCommon {
+ id: BlockId::from_str("00e8829a-a7b8-4075-884a-8f53be145d2f").unwrap(),
+ created_time: DateTime::from_str("2022-05-13T20:08:00.000Z").unwrap(),
+ last_edited_time: DateTime::from_str("2022-05-13T20:08:00.000Z").unwrap(),
+ has_children: true,
+ created_by: UserCommon {
+ id: UserId::from_str("e2507360-468c-4e0f-a928-7bbcbbb45353").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ last_edited_by: UserCommon {
+ id: UserId::from_str("e2507360-468c-4e0f-a928-7bbcbbb45353").unwrap(),
+ name: None,
+ avatar_url: None,
+ },
+ },
+ callout: Callout {
+ rich_text: vec![
+ RichText::Text {
+ rich_text: RichTextCommon {
+ plain_text: "Test callout".to_string(),
+ href: None,
+ annotations: Some(Annotations {
+ bold: Some(false),
+ code: Some(false),
+ color: Some(TextColor::Default),
+ italic: Some(false),
+ strikethrough: Some(false),
+ underline: Some(false),
+ }),
+ },
+ text: Text { content: "Test callout".to_string(), link: None },
+ }
+ ],
+ icon: FileOrEmojiObject::Emoji {
+ emoji: "💡".to_string()
+ },
+ color: TextColor::Green,
+ },
+ }
+ })
+} \ No newline at end of file
diff --git a/src/models/tests/callout.json b/src/models/tests/callout.json
new file mode 100644
index 0000000..e4d884d
--- /dev/null
+++ b/src/models/tests/callout.json
@@ -0,0 +1,43 @@
+{
+ "object": "block",
+ "id": "00e8829a-a7b8-4075-884a-8f53be145d2f",
+ "created_time": "2022-05-13T20:08:00.000Z",
+ "last_edited_time": "2022-05-13T20:08:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "e2507360-468c-4e0f-a928-7bbcbbb45353"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "e2507360-468c-4e0f-a928-7bbcbbb45353"
+ },
+ "has_children": true,
+ "archived": false,
+ "type": "callout",
+ "callout": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "Test callout",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "Test callout",
+ "href": null
+ }
+ ],
+ "icon": {
+ "type": "emoji",
+ "emoji": "💡"
+ },
+ "color": "green"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/emoji_object.json b/src/models/tests/emoji_object.json
new file mode 100644
index 0000000..1fb3b56
--- /dev/null
+++ b/src/models/tests/emoji_object.json
@@ -0,0 +1,4 @@
+{
+ "type": "emoji",
+ "emoji": "💡"
+} \ No newline at end of file
diff --git a/src/models/tests/external_file_object.json b/src/models/tests/external_file_object.json
new file mode 100644
index 0000000..b5d4b85
--- /dev/null
+++ b/src/models/tests/external_file_object.json
@@ -0,0 +1,6 @@
+{
+ "type": "external",
+ "external": {
+ "url": "https://nerdist.com/wp-content/uploads/2020/07/maxresdefault.jpg"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/file_object.json b/src/models/tests/file_object.json
new file mode 100644
index 0000000..650cf9b
--- /dev/null
+++ b/src/models/tests/file_object.json
@@ -0,0 +1,7 @@
+{
+ "type": "file",
+ "file": {
+ "url": "https://s3.us-west-2.amazonaws.com/secure.notion-static.com/2703e742-ace5-428c-a74d-1c587ceddc32/DiRT_Rally.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Content-Sha256=UNSIGNED-PAYLOAD&X-Amz-Credential=AKIAT73L2G45EIPT3X45%2F20220513%2Fus-west-2%2Fs3%2Faws4_request&X-Amz-Date=20220513T201035Z&X-Amz-Expires=3600&X-Amz-Signature=714b49bde0b499fb8f3aae1a88a8cbd374f2b09c1d128e91cac49e85ce0e00fb&X-Amz-SignedHeaders=host&x-id=GetObject",
+ "expiry_time": "2022-05-13T21:10:35.817Z"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/heading_1.json b/src/models/tests/heading_1.json
new file mode 100644
index 0000000..ab2d7e1
--- /dev/null
+++ b/src/models/tests/heading_1.json
@@ -0,0 +1,175 @@
+{
+ "object": "block",
+ "id": "9e891834-6a03-475c-a2b8-421e17f0f3aa",
+ "created_time": "2022-05-12T21:15:00.000Z",
+ "last_edited_time": "2022-05-12T22:10:00.000Z",
+ "created_by": {
+ "object": "user",
+ "id": "6419f912-5293-4ea8-b2c8-9c3ce44f90e3"
+ },
+ "last_edited_by": {
+ "object": "user",
+ "id": "6419f912-5293-4ea8-b2c8-9c3ce44f90e3"
+ },
+ "has_children": false,
+ "archived": false,
+ "type": "heading_1",
+ "heading_1": {
+ "rich_text": [
+ {
+ "type": "text",
+ "text": {
+ "content": "This",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": true,
+ "color": "default"
+ },
+ "plain_text": "This",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "is",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": true,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "is",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "a",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": true,
+ "strikethrough": false,
+ "underline": true,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "a",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "Heading",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": true,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "Heading",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": " ",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": " ",
+ "href": null
+ },
+ {
+ "type": "text",
+ "text": {
+ "content": "1",
+ "link": null
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": true,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "1",
+ "href": null
+ }
+ ],
+ "color": "default"
+ }
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date.json b/src/models/tests/rich_text_mention_date.json
new file mode 100644
index 0000000..687f6dc
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-04-16",
+ "end": null,
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-04-16 → ",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_end.json b/src/models/tests/rich_text_mention_date_with_end.json
new file mode 100644
index 0000000..b4953a0
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_end.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-05-12",
+ "end": "2022-05-13",
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-05-12 → 2022-05-13",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_end_and_time.json b/src/models/tests/rich_text_mention_date_with_end_and_time.json
new file mode 100644
index 0000000..2070207
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_end_and_time.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-04-16T12:00:00.000-04:00",
+ "end": "2022-04-16T12:00:00.000-04:00",
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-04-16T12:00:00.000-04:00 → 2022-04-16T12:00:00.000-04:00",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_date_with_time.json b/src/models/tests/rich_text_mention_date_with_time.json
new file mode 100644
index 0000000..127d9bd
--- /dev/null
+++ b/src/models/tests/rich_text_mention_date_with_time.json
@@ -0,0 +1,21 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "date",
+ "date": {
+ "start": "2022-05-14T09:00:00.000-04:00",
+ "end": null,
+ "time_zone": null
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "2022-05-14T09:00:00.000-04:00 → ",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_mention_user_person.json b/src/models/tests/rich_text_mention_user_person.json
new file mode 100644
index 0000000..8851266
--- /dev/null
+++ b/src/models/tests/rich_text_mention_user_person.json
@@ -0,0 +1,26 @@
+{
+ "type": "mention",
+ "mention": {
+ "type": "user",
+ "user": {
+ "object": "user",
+ "id": "1118608e-35e8-4fa3-aef7-a4ced85ce8e0",
+ "name": "John Doe",
+ "avatar_url": "https://secure.notion-static.com/e6a352a8-8381-44d0-a1dc-9ed80e62b53d.jpg",
+ "type": "person",
+ "person": {
+ "email": "john.doe@gmail.com"
+ }
+ }
+ },
+ "annotations": {
+ "bold": false,
+ "italic": false,
+ "strikethrough": false,
+ "underline": false,
+ "code": false,
+ "color": "default"
+ },
+ "plain_text": "@John Doe",
+ "href": null
+} \ No newline at end of file
diff --git a/src/models/tests/rich_text_text.json b/src/models/tests/rich_text_text.json
new file mode 100644
index 0000000..0089b47
--- /dev/null
+++ b/src/models/tests/rich_text_text.json
@@ -0,0 +1,19 @@
+{
+ "type": "text",
+ "text": {
+ "content": "Rich",
+ "link": {
+ "url": "https://github.com/jakeswenson/notion"
+ }
+ },
+ "annotations": {
+ "bold": true,
+ "italic": true,
+ "strikethrough": true,
+ "underline": true,
+ "code": true,
+ "color": "default"
+ },
+ "plain_text": "Rich",
+ "href": "https://github.com/jakeswenson/notion"
+} \ No newline at end of file
diff --git a/src/models/text.rs b/src/models/text.rs
index 16274ab..4169374 100644
--- a/src/models/text.rs
+++ b/src/models/text.rs
@@ -1,4 +1,7 @@
use serde::{Deserialize, Serialize};
+use crate::models::users::User;
+use crate::{Database, Page};
+use crate::models::properties::DateValue;
#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Copy, Clone)]
#[serde(rename_all = "snake_case")]
@@ -56,6 +59,33 @@ pub struct Text {
pub link: Option<Link>,
}
+/// See https://developers.notion.com/reference/rich-text#mention-objects
+#[derive(Serialize, Deserialize, Debug, Eq, PartialEq, Clone)]
+#[serde(tag = "type")]
+#[serde(rename_all = "snake_case")]
+pub enum MentionObject {
+ User {
+ user: User
+ },
+ // TODO: need to add tests
+ Page {
+ page: Page
+ },
+ // TODO: need to add tests
+ Database {
+ database: Database
+ },
+ Date {
+ date: DateValue
+ },
+ // TODO: need to add LinkPreview
+ // LinkPreview {
+ //
+ // },
+ #[serde(other)]
+ Unknown
+}
+
/// Rich text objects contain data for displaying formatted text, mentions, and equations.
/// A rich text object also contains annotations for style information.
/// Arrays of rich text objects are used within property objects and property
@@ -74,6 +104,7 @@ pub enum RichText {
Mention {
#[serde(flatten)]
rich_text: RichTextCommon,
+ mention: MentionObject
},
/// See <https://developers.notion.com/reference/rich-text#equation-objects>
Equation {