diff --git a/crates/dto/src/entity/tag.rs b/crates/dto/src/entity/tag.rs index 4d33d83..8603272 100644 --- a/crates/dto/src/entity/tag.rs +++ b/crates/dto/src/entity/tag.rs @@ -1,8 +1,8 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 use migration::types::*; -use sea_orm::ActiveValue::Set; use sea_orm::entity::prelude::*; +use sea_orm::ActiveValue::Set; use std::{future::ready, pin::Pin}; #[sea_orm::model] diff --git a/crates/dto/src/entity/zettel.rs b/crates/dto/src/entity/zettel.rs index 6c0cec3..0ed74f9 100644 --- a/crates/dto/src/entity/zettel.rs +++ b/crates/dto/src/entity/zettel.rs @@ -1,8 +1,8 @@ //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 use migration::types::*; -use sea_orm::ActiveValue::Set; use sea_orm::entity::prelude::*; +use sea_orm::ActiveValue::Set; use std::{future::ready, pin::Pin}; #[sea_orm::model] diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 9115a17..28c0453 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -51,6 +51,26 @@ impl eframe::App for FilViz { egui::CentralPanel::default().show_inside(ui, |ui| { ui.heading("Hello World!"); ui.text_edit_singleline(&mut self.text); + + // credits! + ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { + powered_by_egui_and_eframe(ui); + egui::warn_if_debug_build(ui); + }); }); } } + +fn powered_by_egui_and_eframe(ui: &mut egui::Ui) { + ui.horizontal(|ui| { + ui.spacing_mut().item_spacing.x = 0.0; + ui.label("Powered by "); + ui.hyperlink_to("egui", "https://github.com/emilk/egui"); + ui.label(" and "); + ui.hyperlink_to( + "eframe", + "https://github.com/emilk/egui/tree/master/crates/eframe", + ); + ui.label("."); + }); +} diff --git a/src/main.rs b/src/main.rs index 49ea0b4..e50b89c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -36,8 +36,11 @@ fn main() -> color_eyre::Result<()> { // then we spawn the tui on its own thread let tui_handle = std::thread::spawn({ + // arc stuff let tui_rt = rt.clone(); let shutdown = shutdown_signal.clone(); + + // closure to run the tui move || -> color_eyre::Result<()> { // block the tui on the same runtime as above tui_rt.block_on(async { -- 2.54.0 From 0d4d8c48fd4a4018e2bfc388df72adae0a321fc0 Mon Sep 17 00:00:00 2001 From: suri-codes Date: Sat, 28 Mar 2026 10:12:06 -0700 Subject: [PATCH 2/4] feat: created application types --- .../src/m20260319_002245_task_table.rs | 4 ++ crates/dto/src/entity/task.rs | 1 + crates/dto/src/lib.rs | 6 +++ src/main.rs | 1 + src/types/color.rs | 13 +++++ src/types/group.rs | 43 +++++++++++++++ src/types/mod.rs | 22 ++++++++ src/types/priority.rs | 13 +++++ src/types/tag.rs | 41 +++++++++++++++ src/types/task.rs | 52 +++++++++++++++++++ src/types/workspace.rs | 5 ++ src/types/zettel.rs | 39 ++++++++++++++ 12 files changed, 240 insertions(+) create mode 100644 src/types/color.rs create mode 100644 src/types/group.rs create mode 100644 src/types/mod.rs create mode 100644 src/types/priority.rs create mode 100644 src/types/tag.rs create mode 100644 src/types/task.rs create mode 100644 src/types/workspace.rs create mode 100644 src/types/zettel.rs diff --git a/crates/dto/migration/src/m20260319_002245_task_table.rs b/crates/dto/migration/src/m20260319_002245_task_table.rs index 111ced3..35e2222 100644 --- a/crates/dto/migration/src/m20260319_002245_task_table.rs +++ b/crates/dto/migration/src/m20260319_002245_task_table.rs @@ -31,6 +31,7 @@ impl MigrationTrait for Migration { .default(Priority::default().to_string()), ) .col(timestamp(Task::Due).null()) + .col(timestamp(Task::FinishedAt).null()) .col(timestamp(Task::CreatedAt).default(Expr::current_timestamp())) .col(timestamp(Task::ModifiedAt).default(Expr::current_timestamp())) .col(string(Task::ZettelId).not_null().unique_key()) @@ -132,6 +133,9 @@ enum Task { /// The duedate for this task Due, + /// Completion time + FinishedAt, + /// Creation time CreatedAt, diff --git a/crates/dto/src/entity/task.rs b/crates/dto/src/entity/task.rs index 3f980c5..28408b8 100644 --- a/crates/dto/src/entity/task.rs +++ b/crates/dto/src/entity/task.rs @@ -17,6 +17,7 @@ pub struct Model { pub name: String, pub priority: Priority, pub due: Option, + pub finished_at: Option, pub created_at: DateTimeUtc, pub modified_at: DateTimeUtc, #[sea_orm(unique)] diff --git a/crates/dto/src/lib.rs b/crates/dto/src/lib.rs index fad46eb..f706ce8 100644 --- a/crates/dto/src/lib.rs +++ b/crates/dto/src/lib.rs @@ -5,6 +5,10 @@ mod db; pub use db::*; +/// For database stuff +pub use migration::{Migrator, MigratorTrait}; +pub use sea_orm::{Database, DatabaseConnection}; + /// exported traits for the database pub use sea_orm::ActiveModelTrait; pub use sea_orm::ActiveValue; @@ -15,6 +19,8 @@ pub use migration::types::NanoId; /// and add additional functionality to it. pub use migration::types::Priority as PriorityDTO; +pub use sea_orm::entity::prelude::DateTimeUtc; + /// Color type, exporting as DTO because I might /// want to newtype wrap this, might not have to, depending /// on how I end up using it in the application. diff --git a/src/main.rs b/src/main.rs index e50b89c..7d16af5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -13,6 +13,7 @@ use clap::Parser; mod cli; mod gui; mod tui; +mod types; mod config; mod errors; diff --git a/src/types/color.rs b/src/types/color.rs new file mode 100644 index 0000000..e90cb47 --- /dev/null +++ b/src/types/color.rs @@ -0,0 +1,13 @@ +use dto::ColorDTO; + +/// Agnostic Color type, +/// internally represented as rgb +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Color(ColorDTO); + +impl From for Color { + fn from(value: ColorDTO) -> Self { + Self(value) + } +} diff --git a/src/types/group.rs b/src/types/group.rs new file mode 100644 index 0000000..ef7c6b6 --- /dev/null +++ b/src/types/group.rs @@ -0,0 +1,43 @@ +use dto::{DateTimeUtc, GroupModelEx, NanoId}; + +use crate::types::{Color, Priority, Zettel}; + +/// A `Group` which contains tasks! +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Group { + /// Should only be constructed from models. + _private: (), + + pub id: NanoId, + pub name: String, + pub color: Color, + pub priority: Priority, + pub created_at: DateTimeUtc, + pub modified_at: DateTimeUtc, + /// The `Zettel` that is related to this `Group`. + /// Can store notes regarding this group in + /// the `Zettel` + pub zettel: Zettel, +} + +impl From for Group { + fn from(value: GroupModelEx) -> Self { + Self { + _private: (), + id: value.nano_id, + name: value.name, + color: value.color.into(), + priority: value.priority.into(), + created_at: value.created_at, + modified_at: value.modified_at, + zettel: value + .zettel + .into_option() + .expect( + "When fetching a Group from the database, we expect to always have the Zettel loaded!!", + ) + .into(), + } + } +} diff --git a/src/types/mod.rs b/src/types/mod.rs new file mode 100644 index 0000000..aca9380 --- /dev/null +++ b/src/types/mod.rs @@ -0,0 +1,22 @@ +mod color; +pub use color::Color; + +mod tag; +pub use tag::Tag; + +mod priority; +pub use priority::Priority; + +mod zettel; +pub use zettel::Zettel; + +mod group; +pub use group::Group; + +mod task; +#[expect(unused_imports)] +pub use task::Task; + +mod workspace; +#[expect(unused_imports)] +pub use workspace::Workspace; diff --git a/src/types/priority.rs b/src/types/priority.rs new file mode 100644 index 0000000..7727cff --- /dev/null +++ b/src/types/priority.rs @@ -0,0 +1,13 @@ +use dto::PriorityDTO; + +/// An Enum for the various `Priority` levels +/// for `Task`s and `Group`s +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Priority(PriorityDTO); + +impl From for Priority { + fn from(value: PriorityDTO) -> Self { + Self(value) + } +} diff --git a/src/types/tag.rs b/src/types/tag.rs new file mode 100644 index 0000000..7c37575 --- /dev/null +++ b/src/types/tag.rs @@ -0,0 +1,41 @@ +use dto::{NanoId, TagModel, TagModelEx}; + +use crate::types::Color; + +/// Represents a `Tag` in a `ZettelKasten` note taking method. +/// Easy way to link multiple notes under one simple word. +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Tag { + /// Should only be constructed from models. + _private: (), + + /// A unique `NanoId` + pub id: NanoId, + /// Name of the tag + pub name: String, + /// Color of the tag + pub color: Color, +} + +impl From for Tag { + fn from(value: TagModel) -> Self { + Self { + _private: (), + id: value.nano_id, + name: value.name, + color: value.color.into(), + } + } +} + +impl From for Tag { + fn from(value: TagModelEx) -> Self { + Self { + _private: (), + id: value.nano_id, + name: value.name, + color: value.color.into(), + } + } +} diff --git a/src/types/task.rs b/src/types/task.rs new file mode 100644 index 0000000..3c88ab3 --- /dev/null +++ b/src/types/task.rs @@ -0,0 +1,52 @@ +use dto::{DateTimeUtc, NanoId, TaskModelEx}; + +use crate::types::{Group, Priority, Zettel}; + +/// a `Task` that you have to complete! +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Task { + /// Should only be constructed from models. + _private:(), + + pub id: NanoId, + pub name: String, + pub priority: Priority, + pub due: Option, + pub finished_at: Option, + pub created_at: DateTimeUtc, + pub modified_at: DateTimeUtc, + /// Each task has its own related `Zettel`. + pub zettel: Zettel, + pub group: Group, +} + +impl From for Task { + fn from(value: TaskModelEx) -> Self { + Self { + _private: (), + id: value.nano_id, + name: value.name, + priority: value.priority.into(), + due: value.due, + finished_at: value.finished_at, + created_at: value.created_at, + modified_at: value.modified_at, + zettel: value + .zettel + .into_option() + .expect( + "When fetching a Task from the database, we expect to always have the Zettel loaded!!", + ) + .into(), + group: value + .group + .into_option() + .expect( + "When fetching a Task from the database, we expect to always have the Group loaded!!", + ) + .into(), + + } + } +} diff --git a/src/types/workspace.rs b/src/types/workspace.rs new file mode 100644 index 0000000..9b464bd --- /dev/null +++ b/src/types/workspace.rs @@ -0,0 +1,5 @@ +use dto::DatabaseConnection; + +pub struct Workspace { + db: DatabaseConnection, +} diff --git a/src/types/zettel.rs b/src/types/zettel.rs new file mode 100644 index 0000000..ea48014 --- /dev/null +++ b/src/types/zettel.rs @@ -0,0 +1,39 @@ +use std::path::PathBuf; + +use dto::{NanoId, ZettelModelEx}; + +use crate::types::Tag; + +/// A `Zettel` is a note about a single idea. +/// It can have many `Tag`s, just meaning it can fall under many +/// categories. +#[expect(dead_code)] +#[derive(Debug, Clone)] +pub struct Zettel { + /// Should only be constructed from models. + _private: (), + + pub id: NanoId, + pub title: String, + /// a workspace-local file path, needs to be canonicalized before usage + pub file_path: PathBuf, + pub tags: Vec, +} + +impl From for Zettel { + fn from(value: ZettelModelEx) -> Self { + assert!( + !value.tags.is_unloaded(), + "When fetching a Zettel from the database, we expect + to always have the tags loaded!!" + ); + + Self { + _private: (), + id: value.nano_id, + title: value.title, + file_path: value.file_path.into(), + tags: value.tags.into_iter().map(Into::into).collect(), + } + } +} -- 2.54.0 From 98ca8769071af85bfaa93753eb1fb3627fb4a251 Mon Sep 17 00:00:00 2001 From: suri-codes Date: Sat, 28 Mar 2026 12:20:49 -0700 Subject: [PATCH 3/4] feat: workspace + better closing behavior for visualizer! --- flake.lock | 12 +++--- src/cli/process.rs | 12 ++---- src/gui/mod.rs | 20 ++------- src/main.rs | 16 +++---- src/types/mod.rs | 1 - src/types/workspace.rs | 98 +++++++++++++++++++++++++++++++++++++++++- 6 files changed, 113 insertions(+), 46 deletions(-) diff --git a/flake.lock b/flake.lock index 501ddc5..f7ee214 100644 --- a/flake.lock +++ b/flake.lock @@ -8,11 +8,11 @@ "rust-analyzer-src": "rust-analyzer-src" }, "locked": { - "lastModified": 1774555695, - "narHash": "sha256-JpTx7Rn8sILuXAH9a0K0UCZST5KY9ZTMzrZ61KcsNno=", + "lastModified": 1774682177, + "narHash": "sha256-OVbuJnJLlbHE28eRMudjtA6NXz/ifuXSho79gvh6GHY=", "owner": "nix-community", "repo": "fenix", - "rev": "d76ca95395662ed18b02b894e487eb41fd0e7d99", + "rev": "e0f515387df77b9fdbaaf81e7f866f0365474c18", "type": "github" }, "original": { @@ -44,11 +44,11 @@ "rust-analyzer-src": { "flake": false, "locked": { - "lastModified": 1774454876, - "narHash": "sha256-bwkM8HseUs/22x+hy6FWvJMP6q/2CKBrm4sYxz9rMY8=", + "lastModified": 1774569884, + "narHash": "sha256-E8iWEPzg7OnE0XXXjo75CX7xFauqzJuGZ5wSO9KS8Ek=", "owner": "rust-lang", "repo": "rust-analyzer", - "rev": "9253d39eab8b9c9da3c1412fc94764e01d55a02b", + "rev": "443ddcddd0c73b07b799d052f5ef3b448c2f3508", "type": "github" }, "original": { diff --git a/src/cli/process.rs b/src/cli/process.rs index c4fd70b..f4514f7 100644 --- a/src/cli/process.rs +++ b/src/cli/process.rs @@ -9,10 +9,11 @@ use color_eyre::eyre::{Context, Result}; use crate::{ cli::Commands, config::{Config, get_config_dir}, + types::Workspace, }; impl Commands { - pub fn process(self) -> Result<()> { + pub async fn process(self) -> Result<()> { match self { Self::Init { name } => { // create the directory @@ -20,14 +21,7 @@ impl Commands { .context("Failed to get current directory")? .join(&name); - // create the .filaments folder - let filaments_dir = dir.join(".filaments"); - - create_dir_all(&filaments_dir) - .context("Failed to create the filaments directory!")?; - - // create the database inside there - File::create(filaments_dir.join("filaments.db"))?; + Workspace::initialize(dir.clone()).await?; // write config that sets the filaments directory to current dir! let config_kdl = dbg! {Config::generate(&dir)}; diff --git a/src/gui/mod.rs b/src/gui/mod.rs index 28c0453..aea2471 100644 --- a/src/gui/mod.rs +++ b/src/gui/mod.rs @@ -1,41 +1,31 @@ -use std::{ - process, - sync::{ - Arc, - atomic::{AtomicBool, Ordering}, - }, -}; - use eframe::egui; /// The `Filaments Visualizer`, which is an instance of `eframe`, which uses `egui` #[derive(Default)] pub struct FilViz { - shutdown_signal: Arc, /// example for now text: String, } impl FilViz { /// Create a new instance of the `FiLViz` - const fn new(_cc: &eframe::CreationContext<'_>, shutdown_signal: Arc) -> Self { + const fn new(_cc: &eframe::CreationContext<'_>) -> Self { // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style. // Restore app state using cc.storage (requires the "persistence" feature). // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use // for e.g. egui::PaintCallback. Self { - shutdown_signal, text: String::new(), } } /// Create and run the `FilViz`. - pub fn run(shutdown_signal: Arc) -> color_eyre::Result<()> { + pub fn run() -> color_eyre::Result<()> { let native_options = eframe::NativeOptions::default(); eframe::run_native( "Filaments Visualizer", native_options, - Box::new(|cc| Ok(Box::new(Self::new(cc, shutdown_signal)))), + Box::new(|cc| Ok(Box::new(Self::new(cc)))), )?; Ok(()) @@ -44,10 +34,6 @@ impl FilViz { impl eframe::App for FilViz { fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) { - if self.shutdown_signal.load(Ordering::Relaxed) { - process::exit(0) - } - egui::CentralPanel::default().show_inside(ui, |ui| { ui.heading("Hello World!"); ui.text_edit_singleline(&mut self.text); diff --git a/src/main.rs b/src/main.rs index 7d16af5..0049dcd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,10 +2,7 @@ //! My (suri.codes) personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities. //! -use std::sync::{ - Arc, - atomic::{AtomicBool, Ordering}, -}; +use std::{process, sync::Arc}; use crate::{cli::Cli, gui::FilViz, tui::TuiApp}; use clap::Parser; @@ -30,16 +27,13 @@ fn main() -> color_eyre::Result<()> { // if there are any commands, run those and exit if let Some(command) = args.command { - return rt.block_on(async { command.process() }); + return rt.block_on(async { command.process().await }); } - let shutdown_signal = Arc::new(AtomicBool::new(false)); - // then we spawn the tui on its own thread let tui_handle = std::thread::spawn({ // arc stuff let tui_rt = rt.clone(); - let shutdown = shutdown_signal.clone(); // closure to run the tui move || -> color_eyre::Result<()> { @@ -47,8 +41,8 @@ fn main() -> color_eyre::Result<()> { tui_rt.block_on(async { let mut tui = TuiApp::new(args.tick_rate, args.frame_rate)?; tui.run().await?; - shutdown.store(true, Ordering::Relaxed); - Ok(()) + // just close everything as soon as the tui is done running + process::exit(0); }) } }); @@ -57,7 +51,7 @@ fn main() -> color_eyre::Result<()> { if args.visualizer { // enter the guard so egui_async works properly let _rt_guard = rt.enter(); - FilViz::run(shutdown_signal)?; + FilViz::run()?; } // join on the tui diff --git a/src/types/mod.rs b/src/types/mod.rs index aca9380..d809ec6 100644 --- a/src/types/mod.rs +++ b/src/types/mod.rs @@ -18,5 +18,4 @@ mod task; pub use task::Task; mod workspace; -#[expect(unused_imports)] pub use workspace::Workspace; diff --git a/src/types/workspace.rs b/src/types/workspace.rs index 9b464bd..1141682 100644 --- a/src/types/workspace.rs +++ b/src/types/workspace.rs @@ -1,5 +1,99 @@ -use dto::DatabaseConnection; +use std::path::PathBuf; +use color_eyre::eyre::{Context, Result}; +use dto::{Database, DatabaseConnection}; +use tokio::fs::{File, create_dir_all}; +use tracing::debug; + +/// The `Workspace` in which the filaments exist. +#[expect(dead_code)] +#[derive(Debug, Clone)] pub struct Workspace { - db: DatabaseConnection, + /// Private field so it can only be instantiated from a `Path` + _private: (), + /// Connection to the sqlite database inside the `Workspace` + pub db: DatabaseConnection, + /// The path to the root of this workspace + pub root: PathBuf, +} + +impl Workspace { + /// Given a path, try to construct a `Workspace` based on its contents. + /// + /// Note: this means that there should already exist a valid `Workspace` + /// at that path. + pub async fn instansiate(path: impl Into) -> Result { + let path = path.into(); + + let db_conn_string = format!( + "sqlite://{}", + path.clone() + .join(".filaments/filaments.db") + .canonicalize() + .context("Invalid Filaments workspace!!")? + .to_string_lossy() + ); + + debug!("connecting to {db_conn_string}"); + + let conn = Database::connect(db_conn_string) + .await + .context("Failed to connect to the database in the filaments workspace!")?; + + Ok(Self { + _private: (), + db: conn, + root: path, + }) + } + + pub async fn initialize(path: impl Into) -> Result { + let path = path.into(); + + // create the .filaments folder + let filaments_dir = path.join(".filaments"); + + create_dir_all(&filaments_dir) + .await + .context("Failed to create the filaments directory!")?; + + // create the database inside there + File::create(filaments_dir.join("filaments.db")).await?; + + Ok(Self::instansiate(path).await.expect( + "Invariant broken. This instantiation call must always work + since we just initialized the workspace.", + )) + } +} + +#[cfg(test)] +mod tests { + use std::{ + fs::{File, create_dir_all}, + path::PathBuf, + }; + + use dto::NanoId; + + use crate::types::Workspace; + + #[tokio::test] + async fn test_instantiation() { + let path = PathBuf::from("/tmp/filaments/.filaments/filaments.db"); + create_dir_all(path.parent().unwrap()).unwrap(); + let _ = File::create(&path).unwrap(); + let _ws = Workspace::instansiate(dbg!(&path.parent().unwrap().parent().unwrap())) + .await + .unwrap(); + } + + #[tokio::test] + async fn test_initialization() { + let path = PathBuf::from(format!("/tmp/filaments/{}", NanoId::default())); + + Workspace::initialize(path) + .await + .expect("Should initialize just fine"); + } } -- 2.54.0 From 7e7c8e26deb0340ad42a1fff47f03f436d9b2a86 Mon Sep 17 00:00:00 2001 From: suri-codes Date: Sat, 28 Mar 2026 13:03:08 -0700 Subject: [PATCH 4/4] feat: remove old db in dto --- crates/dto/src/db.rs | 85 ---------------------------------- crates/dto/src/lib.rs | 4 -- crates/dto/tests/common/mod.rs | 14 ++++-- crates/dto/tests/task.rs | 12 ++--- crates/dto/tests/zettel.rs | 10 ++-- 5 files changed, 21 insertions(+), 104 deletions(-) delete mode 100644 crates/dto/src/db.rs diff --git a/crates/dto/src/db.rs b/crates/dto/src/db.rs deleted file mode 100644 index 84a63e7..0000000 --- a/crates/dto/src/db.rs +++ /dev/null @@ -1,85 +0,0 @@ -use std::{ops::Deref, path::PathBuf}; - -use migration::{Migrator, MigratorTrait as _}; -use sea_orm::{Database, DatabaseConnection}; -use tracing::debug; - -use thiserror::Error; - -/// Database struct -#[derive(Debug)] -pub struct Db { - conn: DatabaseConnection, -} - -pub type DbResult = Result; - -#[derive(Debug, Error)] -pub enum DbError { - #[error("database file not found, tried looking at {not_found_at}")] - NotFound { not_found_at: String }, - #[error("Seaorm Error")] - SeaOrm(#[from] sea_orm::error::DbErr), -} - -impl AsRef for Db { - fn as_ref(&self) -> &DatabaseConnection { - &self.conn - } -} - -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`. - /// - /// # Errors - /// Will error if `path` is not a valid file. - pub async fn connect(path: impl Into) -> DbResult { - let path = path.into(); - - let connection_string = format!( - "sqlite://{}", - path.canonicalize() - .map_err(|_| DbError::NotFound { - not_found_at: path.to_string_lossy().to_string() - })? - .to_string_lossy() - ); - - debug!("connecting to {connection_string}"); - - let conn = Database::connect(connection_string).await?; - - // run all migrations on connection - Migrator::up(&conn, None).await?; - - Ok(Self { conn }) - } -} - -#[cfg(test)] -mod tests { - use std::{ - fs::{File, create_dir_all}, - path::PathBuf, - }; - - use crate::Db; - - #[tokio::test] - async fn test_connect() { - let path = PathBuf::new(); - let _ = Db::connect(&path).await.expect_err("not found"); - - let path = PathBuf::from("/tmp/filaments/test_db.sqlite"); - create_dir_all(path.parent().unwrap()).unwrap(); - let _ = File::create(&path).unwrap(); - let _db = Db::connect(&path).await.unwrap(); - } -} diff --git a/crates/dto/src/lib.rs b/crates/dto/src/lib.rs index f706ce8..bdd6791 100644 --- a/crates/dto/src/lib.rs +++ b/crates/dto/src/lib.rs @@ -1,10 +1,6 @@ //! The DTO's (Data Transfer Objects) used to interact with //! the Database. There is also a simple database struct in here. -/// Database and its Errors -mod db; -pub use db::*; - /// For database stuff pub use migration::{Migrator, MigratorTrait}; pub use sea_orm::{Database, DatabaseConnection}; diff --git a/crates/dto/tests/common/mod.rs b/crates/dto/tests/common/mod.rs index 3c0d71a..21f8f96 100644 --- a/crates/dto/tests/common/mod.rs +++ b/crates/dto/tests/common/mod.rs @@ -3,10 +3,10 @@ use std::{ path::PathBuf, }; -use dto::Db; use rand::RngExt; +use sea_orm::{Database, DatabaseConnection}; -pub async fn fresh_test_db() -> Db { +pub async fn fresh_test_db() -> DatabaseConnection { let rand_id = { let mut rng = rand::rng(); let mut rand_id = [0_u8; 4]; @@ -15,10 +15,16 @@ pub async fn fresh_test_db() -> Db { String::from_utf8(rand_id.to_vec()).unwrap() }; - let path = PathBuf::from(format!("/tmp/filaments/test_db_{rand_id}")); + let path = PathBuf::from(format!("/tmp/filaments/test_db_{rand_id}.db")); create_dir_all(path.parent().unwrap()).unwrap(); let _ = File::create(&path).unwrap(); - Db::connect(&path).await.unwrap() + + let db_conn_string = format!( + "sqlite://{}", + path.clone().canonicalize().unwrap().to_string_lossy() + ); + + Database::connect(db_conn_string).await.unwrap() } diff --git a/crates/dto/tests/task.rs b/crates/dto/tests/task.rs index c0a09d0..ebb07c2 100644 --- a/crates/dto/tests/task.rs +++ b/crates/dto/tests/task.rs @@ -16,7 +16,7 @@ async fn test_group_task_insert() { file_path: Set("/voo/doo".to_owned()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); @@ -26,7 +26,7 @@ async fn test_group_task_insert() { zettel_id: Set(group_zettel.nano_id.clone()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); @@ -36,7 +36,7 @@ async fn test_group_task_insert() { file_path: Set("/voo/doo".to_owned()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); @@ -46,7 +46,7 @@ async fn test_group_task_insert() { zettel_id: Set(task_zettel.nano_id.clone()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); @@ -54,7 +54,7 @@ async fn test_group_task_insert() { .filter_by_nano_id(task.nano_id.clone()) .with(GroupEntity) .with(ZettelEntity) - .one(db.as_ref()) + .one(&db) .await .unwrap() .unwrap(); @@ -63,7 +63,7 @@ async fn test_group_task_insert() { .filter_by_nano_id(group.nano_id.clone()) .with(TaskEntity) .with(ZettelEntity) - .one(db.as_ref()) + .one(&db) .await .unwrap() .unwrap(); diff --git a/crates/dto/tests/zettel.rs b/crates/dto/tests/zettel.rs index b696f42..fa7f7f4 100644 --- a/crates/dto/tests/zettel.rs +++ b/crates/dto/tests/zettel.rs @@ -15,7 +15,7 @@ async fn test_zettel_tag_insert() { color: Set(ColorDTO::new(255, 0, 0)), ..Default::default() } - .insert(&*db) + .insert(&db) .await .unwrap(); @@ -25,7 +25,7 @@ async fn test_zettel_tag_insert() { file_path: Set("/voo/doo".to_owned()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); @@ -38,7 +38,7 @@ async fn test_zettel_tag_insert() { // .set_color("some color"), // ) .add_tag(tag.clone().into_active_model()) - .insert(&*db) + .insert(&db) .await .unwrap(); @@ -49,14 +49,14 @@ async fn test_zettel_tag_insert() { file_path: Set("/voo/doo".to_owned()), ..Default::default() } - .insert(db.as_ref()) + .insert(&db) .await .unwrap(); let zettels_for_tag = TagEntity::load() .filter_by_nano_id(tag.nano_id.clone()) .with(ZettelEntity) - .all(db.as_ref()) + .all(&db) .await .unwrap();