From c6ecc97c0e7020a2cc1dc8054f7e6bf2ae04f1d3 Mon Sep 17 00:00:00 2001 From: suri-codes Date: Fri, 27 Mar 2026 11:14:22 -0700 Subject: [PATCH] feat/db: zettel tag table + many zettel 2 many tag --- .../src/m20260323_002518_zettel_table.rs | 66 ------------------- crates/dto/migration/src/lib.rs | 2 + .../src/m20260323_002518_zettel_table.rs | 8 +++ .../src/m20260327_180618_zettel_tag_table.rs | 56 ++++++++++++++++ crates/dto/src/db.rs | 9 ++- crates/dto/src/entity/mod.rs | 3 + crates/dto/src/entity/prelude.rs | 2 + crates/dto/src/entity/tag.rs | 37 +++++++++++ crates/dto/src/entity/zettel.rs | 8 +-- crates/dto/src/entity/zettel_tag.rs | 31 +++++++++ crates/dto/src/lib.rs | 9 ++- crates/dto/tests/zettel.rs | 64 ++++++++++++++++++ justfile | 2 +- 13 files changed, 224 insertions(+), 73 deletions(-) delete mode 100644 crates/db/migration/src/m20260323_002518_zettel_table.rs create mode 100644 crates/dto/migration/src/m20260327_180618_zettel_tag_table.rs create mode 100644 crates/dto/src/entity/tag.rs create mode 100644 crates/dto/src/entity/zettel_tag.rs create mode 100644 crates/dto/tests/zettel.rs diff --git a/crates/db/migration/src/m20260323_002518_zettel_table.rs b/crates/db/migration/src/m20260323_002518_zettel_table.rs deleted file mode 100644 index a6e2b9e..0000000 --- a/crates/db/migration/src/m20260323_002518_zettel_table.rs +++ /dev/null @@ -1,66 +0,0 @@ -use sea_orm_migration::{prelude::*, schema::*}; - -use crate::types::NANO_ID_LEN; - -#[derive(DeriveMigrationName)] -pub struct Migration; - -#[async_trait::async_trait] -impl MigrationTrait for Migration { - async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .create_table( - Table::create() - .table(Zettel::Table) - .if_not_exists() - .col(pk_auto(Zettel::Id)) - .col( - string(Zettel::NanoId) - .string_len(NANO_ID_LEN as u32) - .unique_key() - .not_null(), - ) - .col(string(Zettel::Title).not_null()) - .col(string(Zettel::FilePath).not_null()) - .to_owned(), - ) - .await?; - - manager - .create_index( - Index::create() - .name("idx_zettel_pub_id") - .table(Zettel::Table) - .col(Zettel::NanoId) - .to_owned(), - ) - .await - } - - async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { - manager - .drop_index(Index::drop().name("idx_zettel_pub_id").to_owned()) - .await?; - - manager - .drop_table(Table::drop().table("post").to_owned()) - .await - } -} - -#[derive(DeriveIden)] -pub enum Zettel { - Table, - - /// Unique integer id - Id, - - /// Unique nano-id that is user-facing - NanoId, - - /// The title of this zettel - Title, - - /// The relative file-path to this `Zettel` - FilePath, -} diff --git a/crates/dto/migration/src/lib.rs b/crates/dto/migration/src/lib.rs index a8c3135..24fd078 100644 --- a/crates/dto/migration/src/lib.rs +++ b/crates/dto/migration/src/lib.rs @@ -6,6 +6,7 @@ mod m20260318_233726_group_table; mod m20260319_002245_task_table; mod m20260323_002518_zettel_table; mod m20260327_175853_tag_table; +mod m20260327_180618_zettel_tag_table; pub struct Migrator; @@ -17,6 +18,7 @@ impl MigratorTrait for Migrator { Box::new(m20260319_002245_task_table::Migration), Box::new(m20260323_002518_zettel_table::Migration), Box::new(m20260327_175853_tag_table::Migration), + Box::new(m20260327_180618_zettel_tag_table::Migration), ] } } diff --git a/crates/dto/migration/src/m20260323_002518_zettel_table.rs b/crates/dto/migration/src/m20260323_002518_zettel_table.rs index 8a4864e..b2c3522 100644 --- a/crates/dto/migration/src/m20260323_002518_zettel_table.rs +++ b/crates/dto/migration/src/m20260323_002518_zettel_table.rs @@ -51,8 +51,16 @@ impl MigrationTrait for Migration { #[derive(DeriveIden)] pub enum Zettel { Table, + + /// Unique integer id Id, + + /// Unique nano-id that is userfacing NanoId, + + /// Title of this zettel Title, + + /// local file path to this `Zettel` FilePath, } diff --git a/crates/dto/migration/src/m20260327_180618_zettel_tag_table.rs b/crates/dto/migration/src/m20260327_180618_zettel_tag_table.rs new file mode 100644 index 0000000..9badbc1 --- /dev/null +++ b/crates/dto/migration/src/m20260327_180618_zettel_tag_table.rs @@ -0,0 +1,56 @@ +use sea_orm_migration::{prelude::*, schema::*}; + +use crate::{m20260323_002518_zettel_table::Zettel, m20260327_175853_tag_table::Tag}; + +#[derive(DeriveMigrationName)] +pub struct Migration; + +#[async_trait::async_trait] +impl MigrationTrait for Migration { + async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .create_table( + Table::create() + .table(ZettelTag::Table) + .if_not_exists() + .col(string(ZettelTag::ZettelNanoId).not_null()) + .col(string(ZettelTag::TagNanoId).not_null()) + .primary_key( + Index::create() + .col(ZettelTag::ZettelNanoId) + .col(ZettelTag::TagNanoId), + ) + .foreign_key( + ForeignKey::create() + .name("fk-zettel_tag-zettel_nano_id") + .from(ZettelTag::Table, ZettelTag::ZettelNanoId) + .to(Zettel::Table, Zettel::NanoId) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .foreign_key( + ForeignKey::create() + .name("fk-zettel_tag-tag_nano_id") + .from(ZettelTag::Table, ZettelTag::TagNanoId) + .to(Tag::Table, Tag::NanoId) + .on_delete(ForeignKeyAction::Cascade) + .on_update(ForeignKeyAction::Cascade), + ) + .to_owned(), + ) + .await + } + + async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> { + manager + .drop_table(Table::drop().table(ZettelTag::Table).to_owned()) + .await + } +} + +#[derive(DeriveIden)] +enum ZettelTag { + Table, + ZettelNanoId, + TagNanoId, +} diff --git a/crates/dto/src/db.rs b/crates/dto/src/db.rs index b940145..84a63e7 100644 --- a/crates/dto/src/db.rs +++ b/crates/dto/src/db.rs @@ -1,4 +1,4 @@ -use std::path::PathBuf; +use std::{ops::Deref, path::PathBuf}; use migration::{Migrator, MigratorTrait as _}; use sea_orm::{Database, DatabaseConnection}; @@ -28,6 +28,13 @@ impl AsRef for Db { } } +impl Deref for Db { + type Target = DatabaseConnection; + fn deref(&self) -> &Self::Target { + &self.conn + } +} + impl Db { /// Connects to the database to the database at `path`. /// diff --git a/crates/dto/src/entity/mod.rs b/crates/dto/src/entity/mod.rs index e2dbb39..c029a58 100644 --- a/crates/dto/src/entity/mod.rs +++ b/crates/dto/src/entity/mod.rs @@ -1,6 +1,9 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 pub mod prelude; + pub mod group; +pub mod tag; pub mod task; pub mod zettel; +pub mod zettel_tag; diff --git a/crates/dto/src/entity/prelude.rs b/crates/dto/src/entity/prelude.rs index 373adbb..ef214b3 100644 --- a/crates/dto/src/entity/prelude.rs +++ b/crates/dto/src/entity/prelude.rs @@ -2,5 +2,7 @@ #![expect(unused_imports)] pub use super::group::Entity as Group; +pub use super::tag::Entity as Tag; pub use super::task::Entity as Task; pub use super::zettel::Entity as Zettel; +pub use super::zettel_tag::Entity as ZettelTag; diff --git a/crates/dto/src/entity/tag.rs b/crates/dto/src/entity/tag.rs new file mode 100644 index 0000000..eef5801 --- /dev/null +++ b/crates/dto/src/entity/tag.rs @@ -0,0 +1,37 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 + +use migration::types::*; +use sea_orm::{ActiveValue::Set, entity::prelude::*}; +use std::{future::ready, pin::Pin}; + +#[sea_orm::model] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "tag")] +pub struct Model { + #[sea_orm(primary_key)] + pub id: i64, + #[sea_orm(unique)] + pub nano_id: NanoId, + pub name: String, + pub color: String, + #[sea_orm(has_many, via = "zettel_tag")] + pub zettels: HasMany, +} + +impl ActiveModelBehavior for ActiveModel { + fn before_save<'life0, 'async_trait, C>( + mut self, + _db: &'life0 C, + insert: bool, + ) -> Pin> + Send + 'async_trait>> + where + C: ConnectionTrait + 'async_trait, + Self: Send + 'async_trait, + 'life0: 'async_trait, + { + if insert && self.nano_id.is_not_set() { + self.nano_id = Set(NanoId::default()); + } + Box::pin(ready(Ok(self))) + } +} diff --git a/crates/dto/src/entity/zettel.rs b/crates/dto/src/entity/zettel.rs index 510a90d..a747802 100644 --- a/crates/dto/src/entity/zettel.rs +++ b/crates/dto/src/entity/zettel.rs @@ -1,10 +1,8 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 use migration::types::*; -use sea_orm::entity::prelude::*; -use sea_orm::ActiveValue::Set; -use std::future::{ready, Future}; -use std::pin::Pin; +use sea_orm::{ActiveValue::Set, entity::prelude::*}; +use std::{future::ready, pin::Pin}; #[sea_orm::model] #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] @@ -21,6 +19,8 @@ pub struct Model { pub group: HasOne, #[sea_orm(has_one)] pub task: HasOne, + #[sea_orm(has_many, via = "zettel_tag")] + pub tags: HasMany, } impl ActiveModelBehavior for ActiveModel { diff --git a/crates/dto/src/entity/zettel_tag.rs b/crates/dto/src/entity/zettel_tag.rs new file mode 100644 index 0000000..b97b920 --- /dev/null +++ b/crates/dto/src/entity/zettel_tag.rs @@ -0,0 +1,31 @@ +//! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 + +use sea_orm::entity::prelude::*; + +#[sea_orm::model] +#[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)] +#[sea_orm(table_name = "zettel_tag")] +pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] + pub zettel_nano_id: String, + #[sea_orm(primary_key, auto_increment = false)] + pub tag_nano_id: String, + #[sea_orm( + belongs_to, + from = "tag_nano_id", + to = "nano_id", + on_update = "Cascade", + on_delete = "Cascade" + )] + pub tag: HasOne, + #[sea_orm( + belongs_to, + from = "zettel_nano_id", + to = "nano_id", + on_update = "Cascade", + on_delete = "Cascade" + )] + pub zettel: HasOne, +} + +impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/dto/src/lib.rs b/crates/dto/src/lib.rs index 37f37e2..77348ad 100644 --- a/crates/dto/src/lib.rs +++ b/crates/dto/src/lib.rs @@ -36,4 +36,11 @@ pub use entity::zettel::ActiveModel as ZettelActiveModel; pub use entity::zettel::ActiveModelEx as ZettelActiveModelEx; pub use entity::zettel::Entity as ZettelEntity; pub use entity::zettel::Model as ZettelModel; -pub use entity::zettel::Model as ZettelModelEx; +pub use entity::zettel::ModelEx as ZettelModelEx; + +/// Everything related to tag's. +pub use entity::tag::ActiveModel as TagActiveModel; +pub use entity::tag::ActiveModelEx as TagActiveModelEx; +pub use entity::tag::Entity as TagEntity; +pub use entity::tag::Model as TagModel; +pub use entity::tag::ModelEx as TagModelEx; diff --git a/crates/dto/tests/zettel.rs b/crates/dto/tests/zettel.rs new file mode 100644 index 0000000..47841ee --- /dev/null +++ b/crates/dto/tests/zettel.rs @@ -0,0 +1,64 @@ +use dto::{ + ActiveModelTrait, ActiveValue::Set, TagActiveModel, TagEntity, TagModel, ZettelActiveModel, + ZettelEntity, ZettelModel, +}; +use sea_orm::IntoActiveModel; + +mod common; + +#[tokio::test] +async fn test_zettel_tag_insert() { + let db = common::fresh_test_db().await; + + let tag: TagModel = TagActiveModel { + name: Set("Penis".to_owned()), + color: Set("Some Color".to_owned()), + ..Default::default() + } + .insert(&*db) + .await + .unwrap(); + + let _: ZettelModel = ZettelActiveModel { + // nano_id: Set(NanoId::default()), + title: Set("something1".to_owned()), + file_path: Set("/voo/doo".to_owned()), + ..Default::default() + } + .insert(db.as_ref()) + .await + .unwrap(); + + let x = ZettelActiveModel::builder() + .set_title("Hello") + .set_file_path("/voo/doo") + // .add_tag( + // TagActiveModel::builder() + // .set_name("Hi") + // .set_color("some color"), + // ) + .add_tag(tag.clone().into_active_model()) + .insert(&*db) + .await + .unwrap(); + + dbg!(x); + let _: ZettelModel = ZettelActiveModel { + // nano_id: Set(NanoId::default()), + title: Set("nomething2".to_owned()), + file_path: Set("/voo/doo".to_owned()), + ..Default::default() + } + .insert(db.as_ref()) + .await + .unwrap(); + + let zettels_for_tag = TagEntity::load() + .filter_by_nano_id(tag.nano_id.clone()) + .with(ZettelEntity) + .all(db.as_ref()) + .await + .unwrap(); + + dbg!(zettels_for_tag); +} diff --git a/justfile b/justfile index 0f73bcd..5a7ae7a 100644 --- a/justfile +++ b/justfile @@ -26,7 +26,7 @@ dev-db := justfile_directory() + "/target/dev.db" dev-db-url := "sqlite:///" + dev-db # build entities from migrations -[working-directory:"crates/db"] +[working-directory:"crates/dto"] entity: # create the dev db rm -f {{dev-db}} -- 2.51.2