notion_into_sqlite/
notion_database.rs

1use anyhow::{anyhow, Result};
2use serde_json::Value;
3use std::collections::HashMap;
4
5/// Types of property values
6/// See https://developers.notion.com/reference/property-value-object
7/// > Possible values are "rich_text", "number", "select", "multi_select", "date",
8/// > "formula", "relation", "rollup", "title", "people", "files", "checkbox","url",
9/// > "email", "phone_number", "created_time", "created_by", "last_edited_time", and "last_edited_by".
10#[derive(Debug, PartialEq)]
11pub enum NotionPropertyType {
12    RichText,
13    Number,
14    Select,
15    MultiSelect,
16    Date,
17    Formula,
18    Relation,
19    Rollup,
20    Title,
21    People,
22    Files,
23    Checkbox,
24    Url,
25    Email,
26    PhoneNumber,
27    CreatedTime,
28    CreatedBy,
29    LastEditedTime,
30    LastEditedBy,
31    Other,
32}
33
34#[derive(Debug)]
35pub struct NotionProperty {
36    pub name: String,
37    pub property_type: NotionPropertyType,
38    pub property_raw_type: String,
39}
40
41#[derive(Debug)]
42pub struct NotionDatabaseSchema {
43    pub properties: HashMap<String, NotionProperty>,
44}
45
46pub fn parse_database_schema(database_resp: &Value) -> Result<NotionDatabaseSchema> {
47    validate_object_type(database_resp)?;
48
49    let raw_properties = database_resp
50        .as_object()
51        .and_then(|resp| resp.get("properties"))
52        .and_then(|prop| prop.as_object())
53        .ok_or_else(|| anyhow!(r#"It must have "properties" object."#))?;
54
55    let properties = raw_properties
56        .keys()
57        .filter_map(|key| {
58            let property = raw_properties.get(key)?.as_object()?;
59            let name = property.get("name")?.as_str()?;
60            let property_raw_type = property.get("type")?.as_str()?;
61            let property_type = match property_raw_type {
62                "rich_text" => NotionPropertyType::RichText,
63                "number" => NotionPropertyType::Number,
64                "select" => NotionPropertyType::Select,
65                "multi_select" => NotionPropertyType::MultiSelect,
66                "date" => NotionPropertyType::Date,
67                "formula" => NotionPropertyType::Formula,
68                "relation" => NotionPropertyType::Relation,
69                "rollup" => NotionPropertyType::Rollup,
70                "title" => NotionPropertyType::Title,
71                "people" => NotionPropertyType::People,
72                "files" => NotionPropertyType::Files,
73                "checkbox" => NotionPropertyType::Checkbox,
74                "url" => NotionPropertyType::Url,
75                "email" => NotionPropertyType::Email,
76                "phone_number" => NotionPropertyType::PhoneNumber,
77                "created_time" => NotionPropertyType::CreatedTime,
78                "created_by" => NotionPropertyType::CreatedBy,
79                "last_edited_time" => NotionPropertyType::LastEditedTime,
80                "last_edited_by" => NotionPropertyType::LastEditedBy,
81                _ => NotionPropertyType::Other,
82            };
83            Some((
84                name.to_string(),
85                NotionProperty {
86                    name: name.to_string(),
87                    property_raw_type: property_raw_type.to_string(),
88                    property_type,
89                },
90            ))
91        })
92        .collect::<HashMap<String, NotionProperty>>();
93
94    Ok(NotionDatabaseSchema { properties })
95}
96
97fn validate_object_type(database_resp: &Value) -> Result<()> {
98    let object_field = database_resp
99        .as_object()
100        .and_then(|o| o.get("object"))
101        .and_then(|o| o.as_str())
102        .ok_or_else(|| anyhow!(r#"It must have `"object": "database"`."#.to_string()))?;
103
104    if object_field == "database" {
105        Ok(())
106    } else {
107        Err(anyhow!(
108            r#"It must have `"object": "database"`, but was "{}""#,
109            object_field
110        ))
111    }
112}
113
114#[cfg(test)]
115mod tests {
116    use super::*;
117
118    #[test]
119    fn test_validate_object_type() {
120        let data = r#"
121        {
122            "object": "database"
123        }
124        "#;
125        let json = serde_json::from_str(data).unwrap();
126        assert!(validate_object_type(&json).is_ok());
127
128        let data = r#"
129        {
130            "object": "xxx"
131        }
132        "#;
133        let json = serde_json::from_str(data).unwrap();
134        assert!(validate_object_type(&json).is_err());
135
136        let data = r#"
137        {}
138        "#;
139        let json = serde_json::from_str(data).unwrap();
140        assert!(validate_object_type(&json).is_err());
141    }
142}