diff options
Diffstat (limited to 'examples')
-rw-r--r-- | examples/todo/README.md | 19 | ||||
-rw-r--r-- | examples/todo/commands.rs | 1 | ||||
-rw-r--r-- | examples/todo/commands/configure.rs | 70 | ||||
-rw-r--r-- | examples/todo/main.rs | 76 |
4 files changed, 166 insertions, 0 deletions
diff --git a/examples/todo/README.md b/examples/todo/README.md new file mode 100644 index 0000000..b22d08b --- /dev/null +++ b/examples/todo/README.md @@ -0,0 +1,19 @@ +# Notion database todo example + +This example is builds a todo list using a notion database. + +## Setup your notion api token + +Create an `internal` integration here: https://www.notion.so/my-integrations + +```bash + export NOTION_API_TOKEN='secret_token_here' +``` +> Notice the space before the export command. +> This will prevent your terminal from storing this token in your shell history... + +## Selecting the database to use + +```bash +cargo run --example todo -- config +``` diff --git a/examples/todo/commands.rs b/examples/todo/commands.rs new file mode 100644 index 0000000..a78a393 --- /dev/null +++ b/examples/todo/commands.rs @@ -0,0 +1 @@ +pub mod configure; diff --git a/examples/todo/commands/configure.rs b/examples/todo/commands/configure.rs new file mode 100644 index 0000000..42ffafd --- /dev/null +++ b/examples/todo/commands/configure.rs @@ -0,0 +1,70 @@ +use crate::TodoConfig; +use anyhow::Result; +use notion::models::search::NotionSearch; +use notion::models::{Database, DatabaseId}; +use notion::{AsIdentifier, NotionApi}; +use skim::{Skim, SkimItem, SkimItemReceiver, SkimItemSender, SkimOptions}; +use std::borrow::Cow; +use std::ops::Deref; +use std::sync::Arc; + +fn skim_select_database(databases: Vec<Database>) -> Result<DatabaseId> { + let options = SkimOptions::default(); + + let (sender, receiver): (SkimItemSender, SkimItemReceiver) = crossbeam_channel::bounded(500); + + struct SkimDB { + db: Database, + } + + impl SkimItem for SkimDB { + fn text(&self) -> Cow<str> { + Cow::Owned(self.db.title_plain_text()) + } + } + + for db in databases { + sender.send(Arc::new(SkimDB { db }))?; + } + + // `run_with` would read and show items from the stream + let selected_items = Skim::run_with(&options, Some(receiver)) + .filter(|out| !out.is_abort) + .map(|out| out.selected_items) + .unwrap_or_else(|| Vec::new()); + + let db = selected_items + .first() + .expect("No database selected, aborting...") + .clone(); + let db: &SkimDB = db + .deref() + .as_any() + .downcast_ref() + .expect("Couldn't cast back to SkimDB"); + + let database_id = db.db.id(); + + Ok(database_id) +} + +pub async fn configure(notion_api: NotionApi) -> Result<()> { + let databases: Vec<Database> = notion_api + .search(NotionSearch::filter_by_databases()) + .await? + .only_databases() + .results; + + let database_id = skim_select_database(databases)?; + + println!("Selected database's id: {}", database_id); + + let bytes = toml::to_vec(&TodoConfig { + api_token: None, + task_database_id: Some(database_id), + })?; + + std::fs::write("../todo_config.toml", bytes)?; + + Ok(()) +} diff --git a/examples/todo/main.rs b/examples/todo/main.rs new file mode 100644 index 0000000..2283549 --- /dev/null +++ b/examples/todo/main.rs @@ -0,0 +1,76 @@ +mod commands; + +use anyhow::{Context, Result}; +use clap::Clap; +use notion::models::DatabaseId; +use notion::NotionApi; +use serde::{Deserialize, Serialize}; + +// https://docs.rs/clap/3.0.0-beta.2/clap/ +#[derive(Clap)] +#[clap(version = "1.0", author = "Kevin K. <kbknapp@gmail.com>")] +struct Opts { + #[clap(subcommand)] + command: SubCommand, +} + +#[derive(Clap)] +enum SubCommand { + /// Configure what database this notion-todo example uses + Config, + /// List all todos + List, + /// Add a todo item to the notion database + Add, + /// Complete a todo item + Check, +} + +#[derive(Deserialize, Serialize)] +struct TodoConfig { + api_token: Option<String>, + task_database_id: Option<DatabaseId>, +} + +#[tokio::main] +async fn main() -> Result<()> { + let opts: Opts = Opts::parse(); + + // https://docs.rs/config/0.11.0/config/ + let config = config::Config::default() + .with_merged(config::File::with_name("todo_config")) + .unwrap_or_default() + .with_merged(config::Environment::with_prefix("NOTION"))?; + + let config: TodoConfig = config.try_into().context("Failed to read config")?; + + let notion_api = NotionApi::new( + std::env::var("NOTION_API_TOKEN") + .or(config + .api_token + .ok_or(anyhow::anyhow!("No api token from config"))) + .context( + "No Notion API token found in either the environment variable \ + `NOTION_API_TOKEN` or the config file!", + )?, + )?; + + match opts.command { + SubCommand::Config => commands::configure::configure(notion_api).await, + SubCommand::List => list_tasks(notion_api), + SubCommand::Add => add_task(notion_api), + SubCommand::Check => complete_task(notion_api), + } +} + +fn list_tasks(_notion_api: NotionApi) -> Result<()> { + Ok(()) +} + +fn add_task(_notion_api: NotionApi) -> Result<()> { + Ok(()) +} + +fn complete_task(_notion_api: NotionApi) -> Result<()> { + Ok(()) +} |