aboutsummaryrefslogtreecommitdiff
path: root/src/models/mod.rs
diff options
context:
space:
mode:
authorGravatar Shashank Pachava <spachava753@gmail.com> 2022-05-18 01:02:56 -0400
committerGravatar GitHub <noreply@github.com> 2022-05-17 22:02:56 -0700
commit8160fea0ffa1f074538ec5fd73b317bc9d023d6e (patch)
treeecd9288aa409051e57695b638f416540eb77cbe5 /src/models/mod.rs
parenta0106b557ee1af347b7c422be3364dd6d0fac945 (diff)
downloadnotion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.tar.gz
notion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.tar.zst
notion-8160fea0ffa1f074538ec5fd73b317bc9d023d6e.zip
Upgrade notion version (#39)
* Upgrade constant * Fix lint * Change module file tree Changed around module file tree, but module structure hasn't changed. Converted models.rs to mod.rs under models folder * Minor refactoring * Add MentionObject for rich text mention types * Add new fields to BlockCommon. Modify existing field model Text. Add heading 1 test Add created_by and last_edited_by fields to BlockCommon. Change field text to rich_text for model Text to handle breaking change in API version 2022-02-22 * Differentiate between unsupported and unknown block types * Change text field to rich_text in paragraph block * Add callout block. Add file and emoji object * Fix as_id for unsupported block * Fix lint issues * Move quote block to follow documentation order. Add color field to TextAndChildren struct * Add color field to ToDoFields struct * Formatting * Add caption field to code block Add caption field to code block. Create enum CodeLanguage for code block. Reorder code block to reflect documentation * Add child database block * Create embed block * Refactor notion file object struct name * Create image block * Create video block * Create file block * Fix video block field * Create pdf block * Change text field to rich_text in TodoFields for Notion API version 2022-02-22 * Create bookmark block * Create divider block * Create table of contents block * Create breadcrumb block * Create column list and column block * Create link preview block * Create template block * Formatting * Create link to page block * Fix ColumnListFields struct * Create table and table row block * Fix AsIdentifier trait impl for Block * Create synced block
Diffstat (limited to 'src/models/mod.rs')
-rw-r--r--src/models/mod.rs700
1 files changed, 700 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 { .. })
+ }
+}