···99use crate::{
1010 cli::Commands,
1111 config::{Config, get_config_dir},
1212+ types::Workspace,
1213};
13141415impl Commands {
1515- pub fn process(self) -> Result<()> {
1616+ pub async fn process(self) -> Result<()> {
1617 match self {
1718 Self::Init { name } => {
1819 // create the directory
···2021 .context("Failed to get current directory")?
2122 .join(&name);
22232323- // create the .filaments folder
2424- let filaments_dir = dir.join(".filaments");
2525-2626- create_dir_all(&filaments_dir)
2727- .context("Failed to create the filaments directory!")?;
2828-2929- // create the database inside there
3030- File::create(filaments_dir.join("filaments.db"))?;
2424+ Workspace::initialize(dir.clone()).await?;
31253226 // write config that sets the filaments directory to current dir!
3327 let config_kdl = dbg! {Config::generate(&dir)};
+3-17
src/gui/mod.rs
···11-use std::{
22- process,
33- sync::{
44- Arc,
55- atomic::{AtomicBool, Ordering},
66- },
77-};
88-91use eframe::egui;
102113/// The `Filaments Visualizer`, which is an instance of `eframe`, which uses `egui`
124#[derive(Default)]
135pub struct FilViz {
1414- shutdown_signal: Arc<AtomicBool>,
156 /// example for now
167 text: String,
178}
1891910impl FilViz {
2011 /// Create a new instance of the `FiLViz`
2121- const fn new(_cc: &eframe::CreationContext<'_>, shutdown_signal: Arc<AtomicBool>) -> Self {
1212+ const fn new(_cc: &eframe::CreationContext<'_>) -> Self {
2213 // Customize egui here with cc.egui_ctx.set_fonts and cc.egui_ctx.set_global_style.
2314 // Restore app state using cc.storage (requires the "persistence" feature).
2415 // Use the cc.gl (a glow::Context) to create graphics shaders and buffers that you can use
2516 // for e.g. egui::PaintCallback.
2617 Self {
2727- shutdown_signal,
2818 text: String::new(),
2919 }
3020 }
31213222 /// Create and run the `FilViz`.
3333- pub fn run(shutdown_signal: Arc<AtomicBool>) -> color_eyre::Result<()> {
2323+ pub fn run() -> color_eyre::Result<()> {
3424 let native_options = eframe::NativeOptions::default();
3525 eframe::run_native(
3626 "Filaments Visualizer",
3727 native_options,
3838- Box::new(|cc| Ok(Box::new(Self::new(cc, shutdown_signal)))),
2828+ Box::new(|cc| Ok(Box::new(Self::new(cc)))),
3929 )?;
40304131 Ok(())
···44344535impl eframe::App for FilViz {
4636 fn ui(&mut self, ui: &mut egui::Ui, _frame: &mut eframe::Frame) {
4747- if self.shutdown_signal.load(Ordering::Relaxed) {
4848- process::exit(0)
4949- }
5050-5137 egui::CentralPanel::default().show_inside(ui, |ui| {
5238 ui.heading("Hello World!");
5339 ui.text_edit_singleline(&mut self.text);
+5-11
src/main.rs
···22//! My (suri.codes) personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
33//!
4455-use std::sync::{
66- Arc,
77- atomic::{AtomicBool, Ordering},
88-};
55+use std::{process, sync::Arc};
96107use crate::{cli::Cli, gui::FilViz, tui::TuiApp};
118use clap::Parser;
···30273128 // if there are any commands, run those and exit
3229 if let Some(command) = args.command {
3333- return rt.block_on(async { command.process() });
3030+ return rt.block_on(async { command.process().await });
3431 }
35323636- let shutdown_signal = Arc::new(AtomicBool::new(false));
3737-3833 // then we spawn the tui on its own thread
3934 let tui_handle = std::thread::spawn({
4035 // arc stuff
4136 let tui_rt = rt.clone();
4242- let shutdown = shutdown_signal.clone();
43374438 // closure to run the tui
4539 move || -> color_eyre::Result<()> {
···4741 tui_rt.block_on(async {
4842 let mut tui = TuiApp::new(args.tick_rate, args.frame_rate)?;
4943 tui.run().await?;
5050- shutdown.store(true, Ordering::Relaxed);
5151- Ok(())
4444+ // just close everything as soon as the tui is done running
4545+ process::exit(0);
5246 })
5347 }
5448 });
···5751 if args.visualizer {
5852 // enter the guard so egui_async works properly
5953 let _rt_guard = rt.enter();
6060- FilViz::run(shutdown_signal)?;
5454+ FilViz::run()?;
6155 }
62566357 // join on the tui
-1
src/types/mod.rs
···1818pub use task::Task;
19192020mod workspace;
2121-#[expect(unused_imports)]
2221pub use workspace::Workspace;
+96-2
src/types/workspace.rs
···11-use dto::DatabaseConnection;
11+use std::path::PathBuf;
22+33+use color_eyre::eyre::{Context, Result};
44+use dto::{Database, DatabaseConnection};
55+use tokio::fs::{File, create_dir_all};
66+use tracing::debug;
2788+/// The `Workspace` in which the filaments exist.
99+#[expect(dead_code)]
1010+#[derive(Debug, Clone)]
311pub struct Workspace {
44- db: DatabaseConnection,
1212+ /// Private field so it can only be instantiated from a `Path`
1313+ _private: (),
1414+ /// Connection to the sqlite database inside the `Workspace`
1515+ pub db: DatabaseConnection,
1616+ /// The path to the root of this workspace
1717+ pub root: PathBuf,
1818+}
1919+2020+impl Workspace {
2121+ /// Given a path, try to construct a `Workspace` based on its contents.
2222+ ///
2323+ /// Note: this means that there should already exist a valid `Workspace`
2424+ /// at that path.
2525+ pub async fn instansiate(path: impl Into<PathBuf>) -> Result<Self> {
2626+ let path = path.into();
2727+2828+ let db_conn_string = format!(
2929+ "sqlite://{}",
3030+ path.clone()
3131+ .join(".filaments/filaments.db")
3232+ .canonicalize()
3333+ .context("Invalid Filaments workspace!!")?
3434+ .to_string_lossy()
3535+ );
3636+3737+ debug!("connecting to {db_conn_string}");
3838+3939+ let conn = Database::connect(db_conn_string)
4040+ .await
4141+ .context("Failed to connect to the database in the filaments workspace!")?;
4242+4343+ Ok(Self {
4444+ _private: (),
4545+ db: conn,
4646+ root: path,
4747+ })
4848+ }
4949+5050+ pub async fn initialize(path: impl Into<PathBuf>) -> Result<Self> {
5151+ let path = path.into();
5252+5353+ // create the .filaments folder
5454+ let filaments_dir = path.join(".filaments");
5555+5656+ create_dir_all(&filaments_dir)
5757+ .await
5858+ .context("Failed to create the filaments directory!")?;
5959+6060+ // create the database inside there
6161+ File::create(filaments_dir.join("filaments.db")).await?;
6262+6363+ Ok(Self::instansiate(path).await.expect(
6464+ "Invariant broken. This instantiation call must always work
6565+ since we just initialized the workspace.",
6666+ ))
6767+ }
6868+}
6969+7070+#[cfg(test)]
7171+mod tests {
7272+ use std::{
7373+ fs::{File, create_dir_all},
7474+ path::PathBuf,
7575+ };
7676+7777+ use dto::NanoId;
7878+7979+ use crate::types::Workspace;
8080+8181+ #[tokio::test]
8282+ async fn test_instantiation() {
8383+ let path = PathBuf::from("/tmp/filaments/.filaments/filaments.db");
8484+ create_dir_all(path.parent().unwrap()).unwrap();
8585+ let _ = File::create(&path).unwrap();
8686+ let _ws = Workspace::instansiate(dbg!(&path.parent().unwrap().parent().unwrap()))
8787+ .await
8888+ .unwrap();
8989+ }
9090+9191+ #[tokio::test]
9292+ async fn test_initialization() {
9393+ let path = PathBuf::from(format!("/tmp/filaments/{}", NanoId::default()));
9494+9595+ Workspace::initialize(path)
9696+ .await
9797+ .expect("Should initialize just fine");
9898+ }
599}