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/db: zettel tag table + many zettel 2 many tag

+224 -73
-66
crates/db/migration/src/m20260323_002518_zettel_table.rs
··· 1 - use sea_orm_migration::{prelude::*, schema::*}; 2 - 3 - use crate::types::NANO_ID_LEN; 4 - 5 - #[derive(DeriveMigrationName)] 6 - pub struct Migration; 7 - 8 - #[async_trait::async_trait] 9 - impl MigrationTrait for Migration { 10 - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 11 - manager 12 - .create_table( 13 - Table::create() 14 - .table(Zettel::Table) 15 - .if_not_exists() 16 - .col(pk_auto(Zettel::Id)) 17 - .col( 18 - string(Zettel::NanoId) 19 - .string_len(NANO_ID_LEN as u32) 20 - .unique_key() 21 - .not_null(), 22 - ) 23 - .col(string(Zettel::Title).not_null()) 24 - .col(string(Zettel::FilePath).not_null()) 25 - .to_owned(), 26 - ) 27 - .await?; 28 - 29 - manager 30 - .create_index( 31 - Index::create() 32 - .name("idx_zettel_pub_id") 33 - .table(Zettel::Table) 34 - .col(Zettel::NanoId) 35 - .to_owned(), 36 - ) 37 - .await 38 - } 39 - 40 - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 41 - manager 42 - .drop_index(Index::drop().name("idx_zettel_pub_id").to_owned()) 43 - .await?; 44 - 45 - manager 46 - .drop_table(Table::drop().table("post").to_owned()) 47 - .await 48 - } 49 - } 50 - 51 - #[derive(DeriveIden)] 52 - pub enum Zettel { 53 - Table, 54 - 55 - /// Unique integer id 56 - Id, 57 - 58 - /// Unique nano-id that is user-facing 59 - NanoId, 60 - 61 - /// The title of this zettel 62 - Title, 63 - 64 - /// The relative file-path to this `Zettel` 65 - FilePath, 66 - }
+2
crates/dto/migration/src/lib.rs
··· 6 6 mod m20260319_002245_task_table; 7 7 mod m20260323_002518_zettel_table; 8 8 mod m20260327_175853_tag_table; 9 + mod m20260327_180618_zettel_tag_table; 9 10 10 11 pub struct Migrator; 11 12 ··· 17 18 Box::new(m20260319_002245_task_table::Migration), 18 19 Box::new(m20260323_002518_zettel_table::Migration), 19 20 Box::new(m20260327_175853_tag_table::Migration), 21 + Box::new(m20260327_180618_zettel_tag_table::Migration), 20 22 ] 21 23 } 22 24 }
+8
crates/dto/migration/src/m20260323_002518_zettel_table.rs
··· 51 51 #[derive(DeriveIden)] 52 52 pub enum Zettel { 53 53 Table, 54 + 55 + /// Unique integer id 54 56 Id, 57 + 58 + /// Unique nano-id that is userfacing 55 59 NanoId, 60 + 61 + /// Title of this zettel 56 62 Title, 63 + 64 + /// local file path to this `Zettel` 57 65 FilePath, 58 66 }
+56
crates/dto/migration/src/m20260327_180618_zettel_tag_table.rs
··· 1 + use sea_orm_migration::{prelude::*, schema::*}; 2 + 3 + use crate::{m20260323_002518_zettel_table::Zettel, m20260327_175853_tag_table::Tag}; 4 + 5 + #[derive(DeriveMigrationName)] 6 + pub struct Migration; 7 + 8 + #[async_trait::async_trait] 9 + impl MigrationTrait for Migration { 10 + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { 11 + manager 12 + .create_table( 13 + Table::create() 14 + .table(ZettelTag::Table) 15 + .if_not_exists() 16 + .col(string(ZettelTag::ZettelNanoId).not_null()) 17 + .col(string(ZettelTag::TagNanoId).not_null()) 18 + .primary_key( 19 + Index::create() 20 + .col(ZettelTag::ZettelNanoId) 21 + .col(ZettelTag::TagNanoId), 22 + ) 23 + .foreign_key( 24 + ForeignKey::create() 25 + .name("fk-zettel_tag-zettel_nano_id") 26 + .from(ZettelTag::Table, ZettelTag::ZettelNanoId) 27 + .to(Zettel::Table, Zettel::NanoId) 28 + .on_delete(ForeignKeyAction::Cascade) 29 + .on_update(ForeignKeyAction::Cascade), 30 + ) 31 + .foreign_key( 32 + ForeignKey::create() 33 + .name("fk-zettel_tag-tag_nano_id") 34 + .from(ZettelTag::Table, ZettelTag::TagNanoId) 35 + .to(Tag::Table, Tag::NanoId) 36 + .on_delete(ForeignKeyAction::Cascade) 37 + .on_update(ForeignKeyAction::Cascade), 38 + ) 39 + .to_owned(), 40 + ) 41 + .await 42 + } 43 + 44 + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { 45 + manager 46 + .drop_table(Table::drop().table(ZettelTag::Table).to_owned()) 47 + .await 48 + } 49 + } 50 + 51 + #[derive(DeriveIden)] 52 + enum ZettelTag { 53 + Table, 54 + ZettelNanoId, 55 + TagNanoId, 56 + }
+8 -1
crates/dto/src/db.rs
··· 1 - use std::path::PathBuf; 1 + use std::{ops::Deref, path::PathBuf}; 2 2 3 3 use migration::{Migrator, MigratorTrait as _}; 4 4 use sea_orm::{Database, DatabaseConnection}; ··· 24 24 25 25 impl AsRef<DatabaseConnection> for Db { 26 26 fn as_ref(&self) -> &DatabaseConnection { 27 + &self.conn 28 + } 29 + } 30 + 31 + impl Deref for Db { 32 + type Target = DatabaseConnection; 33 + fn deref(&self) -> &Self::Target { 27 34 &self.conn 28 35 } 29 36 }
+3
crates/dto/src/entity/mod.rs
··· 1 1 //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 2 2 3 3 pub mod prelude; 4 + 4 5 pub mod group; 6 + pub mod tag; 5 7 pub mod task; 6 8 pub mod zettel; 9 + pub mod zettel_tag;
+2
crates/dto/src/entity/prelude.rs
··· 2 2 #![expect(unused_imports)] 3 3 4 4 pub use super::group::Entity as Group; 5 + pub use super::tag::Entity as Tag; 5 6 pub use super::task::Entity as Task; 6 7 pub use super::zettel::Entity as Zettel; 8 + pub use super::zettel_tag::Entity as ZettelTag;
+37
crates/dto/src/entity/tag.rs
··· 1 + //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 2 + 3 + use migration::types::*; 4 + use sea_orm::{ActiveValue::Set, entity::prelude::*}; 5 + use std::{future::ready, pin::Pin}; 6 + 7 + #[sea_orm::model] 8 + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] 9 + #[sea_orm(table_name = "tag")] 10 + pub struct Model { 11 + #[sea_orm(primary_key)] 12 + pub id: i64, 13 + #[sea_orm(unique)] 14 + pub nano_id: NanoId, 15 + pub name: String, 16 + pub color: String, 17 + #[sea_orm(has_many, via = "zettel_tag")] 18 + pub zettels: HasMany<super::zettel::Entity>, 19 + } 20 + 21 + impl ActiveModelBehavior for ActiveModel { 22 + fn before_save<'life0, 'async_trait, C>( 23 + mut self, 24 + _db: &'life0 C, 25 + insert: bool, 26 + ) -> Pin<Box<dyn Future<Output = Result<Self, DbErr>> + Send + 'async_trait>> 27 + where 28 + C: ConnectionTrait + 'async_trait, 29 + Self: Send + 'async_trait, 30 + 'life0: 'async_trait, 31 + { 32 + if insert && self.nano_id.is_not_set() { 33 + self.nano_id = Set(NanoId::default()); 34 + } 35 + Box::pin(ready(Ok(self))) 36 + } 37 + }
+4 -4
crates/dto/src/entity/zettel.rs
··· 1 1 //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 2 2 3 3 use migration::types::*; 4 - use sea_orm::entity::prelude::*; 5 - use sea_orm::ActiveValue::Set; 6 - use std::future::{ready, Future}; 7 - use std::pin::Pin; 4 + use sea_orm::{ActiveValue::Set, entity::prelude::*}; 5 + use std::{future::ready, pin::Pin}; 8 6 9 7 #[sea_orm::model] 10 8 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] ··· 21 19 pub group: HasOne<super::group::Entity>, 22 20 #[sea_orm(has_one)] 23 21 pub task: HasOne<super::task::Entity>, 22 + #[sea_orm(has_many, via = "zettel_tag")] 23 + pub tags: HasMany<super::tag::Entity>, 24 24 } 25 25 26 26 impl ActiveModelBehavior for ActiveModel {
+31
crates/dto/src/entity/zettel_tag.rs
··· 1 + //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 2 + 3 + use sea_orm::entity::prelude::*; 4 + 5 + #[sea_orm::model] 6 + #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] 7 + #[sea_orm(table_name = "zettel_tag")] 8 + pub struct Model { 9 + #[sea_orm(primary_key, auto_increment = false)] 10 + pub zettel_nano_id: String, 11 + #[sea_orm(primary_key, auto_increment = false)] 12 + pub tag_nano_id: String, 13 + #[sea_orm( 14 + belongs_to, 15 + from = "tag_nano_id", 16 + to = "nano_id", 17 + on_update = "Cascade", 18 + on_delete = "Cascade" 19 + )] 20 + pub tag: HasOne<super::tag::Entity>, 21 + #[sea_orm( 22 + belongs_to, 23 + from = "zettel_nano_id", 24 + to = "nano_id", 25 + on_update = "Cascade", 26 + on_delete = "Cascade" 27 + )] 28 + pub zettel: HasOne<super::zettel::Entity>, 29 + } 30 + 31 + impl ActiveModelBehavior for ActiveModel {}
+8 -1
crates/dto/src/lib.rs
··· 36 36 pub use entity::zettel::ActiveModelEx as ZettelActiveModelEx; 37 37 pub use entity::zettel::Entity as ZettelEntity; 38 38 pub use entity::zettel::Model as ZettelModel; 39 - pub use entity::zettel::Model as ZettelModelEx; 39 + pub use entity::zettel::ModelEx as ZettelModelEx; 40 + 41 + /// Everything related to tag's. 42 + pub use entity::tag::ActiveModel as TagActiveModel; 43 + pub use entity::tag::ActiveModelEx as TagActiveModelEx; 44 + pub use entity::tag::Entity as TagEntity; 45 + pub use entity::tag::Model as TagModel; 46 + pub use entity::tag::ModelEx as TagModelEx;
+64
crates/dto/tests/zettel.rs
··· 1 + use dto::{ 2 + ActiveModelTrait, ActiveValue::Set, TagActiveModel, TagEntity, TagModel, ZettelActiveModel, 3 + ZettelEntity, ZettelModel, 4 + }; 5 + use sea_orm::IntoActiveModel; 6 + 7 + mod common; 8 + 9 + #[tokio::test] 10 + async fn test_zettel_tag_insert() { 11 + let db = common::fresh_test_db().await; 12 + 13 + let tag: TagModel = TagActiveModel { 14 + name: Set("Penis".to_owned()), 15 + color: Set("Some Color".to_owned()), 16 + ..Default::default() 17 + } 18 + .insert(&*db) 19 + .await 20 + .unwrap(); 21 + 22 + let _: ZettelModel = ZettelActiveModel { 23 + // nano_id: Set(NanoId::default()), 24 + title: Set("something1".to_owned()), 25 + file_path: Set("/voo/doo".to_owned()), 26 + ..Default::default() 27 + } 28 + .insert(db.as_ref()) 29 + .await 30 + .unwrap(); 31 + 32 + let x = ZettelActiveModel::builder() 33 + .set_title("Hello") 34 + .set_file_path("/voo/doo") 35 + // .add_tag( 36 + // TagActiveModel::builder() 37 + // .set_name("Hi") 38 + // .set_color("some color"), 39 + // ) 40 + .add_tag(tag.clone().into_active_model()) 41 + .insert(&*db) 42 + .await 43 + .unwrap(); 44 + 45 + dbg!(x); 46 + let _: ZettelModel = ZettelActiveModel { 47 + // nano_id: Set(NanoId::default()), 48 + title: Set("nomething2".to_owned()), 49 + file_path: Set("/voo/doo".to_owned()), 50 + ..Default::default() 51 + } 52 + .insert(db.as_ref()) 53 + .await 54 + .unwrap(); 55 + 56 + let zettels_for_tag = TagEntity::load() 57 + .filter_by_nano_id(tag.nano_id.clone()) 58 + .with(ZettelEntity) 59 + .all(db.as_ref()) 60 + .await 61 + .unwrap(); 62 + 63 + dbg!(zettels_for_tag); 64 + }
+1 -1
justfile
··· 26 26 dev-db-url := "sqlite:///" + dev-db 27 27 28 28 # build entities from migrations 29 - [working-directory:"crates/db"] 29 + [working-directory:"crates/dto"] 30 30 entity: 31 31 # create the dev db 32 32 rm -f {{dev-db}}