My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: create new zettel

+92 -10
+7 -1
crates/dto/tests/common/mod.rs
··· 3 3 path::PathBuf, 4 4 }; 5 5 6 + use dto::{Migrator, MigratorTrait}; 6 7 use rand::RngExt; 7 8 use sea_orm::{Database, DatabaseConnection}; 8 9 ··· 26 27 path.clone().canonicalize().unwrap().to_string_lossy() 27 28 ); 28 29 29 - Database::connect(db_conn_string).await.unwrap() 30 + let conn = Database::connect(db_conn_string).await.unwrap(); 31 + 32 + // run da migrations every time we connect, just in case 33 + Migrator::up(&conn, None).await.unwrap(); 34 + 35 + conn 30 36 }
+22
src/cli/mod.rs
··· 25 25 26 26 #[derive(Subcommand, Debug)] 27 27 pub enum Commands { 28 + /// Manage `Zettel`s 29 + #[command(subcommand)] 30 + Zettel(ZettelSubcommand), 31 + 28 32 // / Manage TARS groups. 29 33 // #[command(subcommand)] 30 34 // Group(GroupSubcommand), ··· 50 54 // Import(ImportArgs), 51 55 } 52 56 57 + #[derive(Subcommand, Debug)] 58 + /// Subcommand to manage tars groups. 59 + pub enum ZettelSubcommand { 60 + /// Add a group. 61 + New { 62 + #[arg(short, long)] 63 + /// The file-path for data to pe put into. 64 + title: String, 65 + }, 66 + /// List groups. 67 + List { 68 + /// The file-path for data to pe put into. 69 + #[arg(short = 't', long)] 70 + by_tag: String, 71 + }, 72 + } 73 + 53 74 // #[derive(Subcommand, Debug)] 54 75 // /// Subcommand to manage tars groups. 55 76 // pub enum GroupSubcommand { ··· 65 86 // /// The file-path for data to pe put into. 66 87 // pub out_file: PathBuf, 67 88 // } 89 + // 68 90 69 91 const VERSION_MESSAGE: &str = concat!( 70 92 env!("CARGO_PKG_VERSION"),
+15 -2
src/cli/process.rs
··· 7 7 use color_eyre::eyre::{Context, Result}; 8 8 9 9 use crate::{ 10 - cli::Commands, 10 + cli::{Commands, ZettelSubcommand}, 11 11 config::{Config, get_config_dir}, 12 - types::Workspace, 12 + types::{Workspace, Zettel}, 13 13 }; 14 14 15 15 impl Commands { ··· 40 40 41 41 // report status! 42 42 println!("Initialized successfully!"); 43 + } 44 + 45 + Self::Zettel(zettel_sub_command) => { 46 + let conf = Config::parse()?; 47 + let ws = Workspace::instansiate(conf.app_config.workspace).await?; 48 + 49 + match zettel_sub_command { 50 + ZettelSubcommand::New { title } => { 51 + let zettel = Zettel::new(title, &ws).await?; 52 + println!("Zettel Created! {zettel:#?}"); 53 + } 54 + ZettelSubcommand::List { by_tag: _by_tag } => {} 55 + } 43 56 } 44 57 } 45 58
+2 -3
src/config.rs
··· 38 38 #[expect(dead_code)] 39 39 pub struct AppConfig { 40 40 /// The directory where the single instance of the filaments exists. 41 - pub filaments: PathBuf, 41 + pub workspace: PathBuf, 42 42 #[serde(default)] 43 43 pub data: PathBuf, 44 44 #[serde(default)] ··· 46 46 } 47 47 48 48 /// Configuration for the App 49 - #[expect(dead_code)] 50 49 #[derive(Debug, Clone)] 51 50 pub struct Config { 52 51 pub app_config: AppConfig, ··· 114 113 115 114 Ok(Self { 116 115 app_config: AppConfig { 117 - filaments: filaments_dir, 116 + workspace: filaments_dir, 118 117 data: get_data_dir(), 119 118 config: get_config_dir(), 120 119 },
+4
src/types/mod.rs
··· 19 19 20 20 mod workspace; 21 21 pub use workspace::Workspace; 22 + 23 + 24 + 25 +
+4 -2
src/types/workspace.rs
··· 1 1 use std::path::PathBuf; 2 2 3 3 use color_eyre::eyre::{Context, Result}; 4 - use dto::{Database, DatabaseConnection}; 4 + use dto::{Database, DatabaseConnection, Migrator, MigratorTrait}; 5 5 use tokio::fs::{File, create_dir_all}; 6 6 use tracing::debug; 7 7 8 8 /// The `Workspace` in which the filaments exist. 9 - #[expect(dead_code)] 10 9 #[derive(Debug, Clone)] 11 10 pub struct Workspace { 12 11 /// Private field so it can only be instantiated from a `Path` ··· 39 38 let conn = Database::connect(db_conn_string) 40 39 .await 41 40 .context("Failed to connect to the database in the filaments workspace!")?; 41 + 42 + // run da migrations every time we connect, just in case 43 + Migrator::up(&conn, None).await?; 42 44 43 45 Ok(Self { 44 46 _private: (),
+38 -2
src/types/zettel.rs
··· 1 + use dto::{TagEntity, ZettelActiveModel, ZettelEntity, ZettelModelEx}; 1 2 use std::path::PathBuf; 2 3 3 - use dto::{NanoId, ZettelModelEx}; 4 + use color_eyre::eyre::Result; 5 + use dto::NanoId; 6 + use tokio::fs::File; 4 7 5 - use crate::types::Tag; 8 + use crate::types::{Tag, Workspace}; 6 9 7 10 /// A `Zettel` is a note about a single idea. 8 11 /// It can have many `Tag`s, just meaning it can fall under many ··· 18 21 /// a workspace-local file path, needs to be canonicalized before usage 19 22 pub file_path: PathBuf, 20 23 pub tags: Vec<Tag>, 24 + } 25 + 26 + impl Zettel { 27 + pub async fn new(title: impl Into<String>, ws: &Workspace) -> Result<Self> { 28 + // fn new(title: impl Into<String>) -> Result<Self> { 29 + let title = title.into(); 30 + 31 + // make a file that has a random identifier, and then 32 + // also has the name "title" 33 + let nano_id = NanoId::default(); 34 + 35 + let local_file_path = format!("{nano_id}.md"); 36 + 37 + // now we have to create the file 38 + File::create_new(ws.root.clone().join(&local_file_path)).await?; 39 + 40 + let inserted = ZettelActiveModel::builder() 41 + .set_title(title) 42 + .set_file_path(local_file_path) 43 + .set_nano_id(nano_id) 44 + .insert(&ws.db) 45 + .await?; 46 + 47 + // need to load tags... 48 + let zettel = ZettelEntity::load() 49 + .filter_by_nano_id(inserted.nano_id) 50 + .with(TagEntity) 51 + .one(&ws.db) 52 + .await? 53 + .expect("This must exist since we just inserted it"); 54 + 55 + Ok(zettel.into()) 56 + } 21 57 } 22 58 23 59 impl From<ZettelModelEx> for Zettel {