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: `fil init` works

+169 -43
+2
.config/config.kdl
··· 1 + 2 + filaments_dir "{INSERT_HERE}" 1 3 2 4 keymap { 3 5
+1
.gitignore
··· 16 16 # Added by cargo 17 17 18 18 /target 19 + /ZettelKasten
+4
Cargo.toml
··· 47 47 readme.workspace = true 48 48 keywords.workspace = true 49 49 50 + [[bin]] 51 + name = "fil" 52 + path = "./src/main.rs" 53 + 50 54 [lints] 51 55 workspace = true 52 56
+1 -1
crates/db/src/entity/group.rs
··· 1 1 //! `SeaORM` Entity, @generated by sea-orm-codegen 2.0 2 2 3 - use sea_orm::entity::prelude::*; 4 3 use migration::types::*; 4 + use sea_orm::entity::prelude::*; 5 5 6 6 #[sea_orm::model] 7 7 #[derive(Clone, Debug, PartialEq, Eq, DeriveEntityModel)]
+4 -4
src/app.rs
··· 40 40 41 41 impl App { 42 42 /// Construct a new `App` instance. 43 - pub fn new(tick_rate: f64, frame_rate: f64) -> Self { 43 + pub fn new(tick_rate: f64, frame_rate: f64) -> Result<Self> { 44 44 let (signal_tx, signal_rx) = mpsc::unbounded_channel(); 45 45 46 - Self { 46 + Ok(Self { 47 47 tick_rate, 48 48 frame_rate, 49 49 components: vec![], 50 50 should_quit: false, 51 51 should_suspend: false, 52 - config: Config::new(), 52 + config: Config::parse()?, 53 53 region: Region::default(), 54 54 last_tick_key_events: Vec::new(), 55 55 signal_tx, 56 56 signal_rx, 57 - } 57 + }) 58 58 } 59 59 60 60 pub async fn run(&mut self) -> Result<()> {
+20 -10
src/cli.rs src/cli/mod.rs
··· 2 2 3 3 use crate::config::{get_config_dir, get_data_dir}; 4 4 5 + mod process; 6 + 5 7 #[derive(Parser, Debug)] 6 8 #[command(author, version = version(), about)] 7 9 pub struct Cli { ··· 19 21 20 22 #[derive(Subcommand, Debug)] 21 23 pub enum Commands { 22 - /// Manage TARS groups. 23 - // #[command(subcommand)] 24 - // Group(GroupSubcommand), 24 + // / Manage TARS groups. 25 + // #[command(subcommand)] 26 + // Group(GroupSubcommand), 25 27 26 - /// Manage TARS tasks. 27 - // #[command(subcommand)] 28 - // Task(TaskSubcommand), 29 - 30 - /// simple testing stuff 31 - Test, 32 - // Imports bulk data into TARS 28 + // / Manage TARS tasks. 29 + // #[command(subcommand)] 30 + // Task(TaskSubcommand), 31 + // 32 + // 33 + /// Initalize Filaments. 34 + /// 35 + /// This will write a default config to ~/.config/filaments, 36 + /// as well as creating a new "notebook" in the current 37 + /// directory with the specified name. Note that we currently 38 + /// only support one notebook. 39 + Init { 40 + #[arg(default_value = "ZettelKasten")] 41 + name: String, 42 + }, 33 43 // NOTE: By default the importer will fill in fields with 34 44 // default values if they arent present / aren't able to be 35 45 // parsed properly
+54
src/cli/process.rs
··· 1 + use std::{ 2 + env::current_dir, 3 + fs::{File, create_dir_all}, 4 + io::Write, 5 + }; 6 + 7 + use color_eyre::eyre::{Context, Result}; 8 + 9 + use crate::{ 10 + cli::Commands, 11 + config::{Config, get_config_dir}, 12 + }; 13 + 14 + impl Commands { 15 + pub fn process(self) -> Result<()> { 16 + match self { 17 + Self::Init { name } => { 18 + // create the directory 19 + let dir = current_dir() 20 + .context("Failed to get current directory")? 21 + .join(&name); 22 + 23 + // create the .filaments folder 24 + let filaments_dir = dir.join(".filaments"); 25 + 26 + create_dir_all(&filaments_dir) 27 + .context("Failed to create the filaments directory!")?; 28 + 29 + // create the database inside there 30 + File::create(filaments_dir.join("filaments.db"))?; 31 + 32 + // write config that sets the filaments directory to current dir! 33 + let config_kdl = dbg! {Config::generate(&dir)}; 34 + 35 + // create the config dir 36 + let config_dir = get_config_dir(); 37 + 38 + create_dir_all(config_dir).expect("creating the config dir should not error"); 39 + 40 + let mut config_file = File::create(get_config_dir().join("config.kdl")) 41 + .context("Failed to create config file")?; 42 + 43 + write!(config_file, "{config_kdl}")?; 44 + 45 + println!("wrote config to {config_file:#?}"); 46 + 47 + // report status! 48 + println!("Initialized successfully!"); 49 + } 50 + } 51 + 52 + Ok(()) 53 + } 54 + }
+76 -17
src/config.rs
··· 1 + use color_eyre::eyre::Context; 1 2 use directories::ProjectDirs; 2 3 use kdl::KdlDocument; 3 4 use serde::Deserialize; 4 - use std::{env, path::PathBuf, sync::LazyLock}; 5 + use std::{ 6 + env::{self, home_dir}, 7 + fmt::Debug, 8 + fs::File, 9 + io::Read, 10 + path::{Path, PathBuf}, 11 + sync::LazyLock, 12 + }; 5 13 6 14 use crate::keymap::KeyMap; 7 15 ··· 29 37 #[derive(Clone, Debug, Deserialize, Default)] 30 38 #[expect(dead_code)] 31 39 pub struct AppConfig { 40 + /// The directory where the single instance of the filaments exists. 41 + pub filaments: PathBuf, 32 42 #[serde(default)] 33 - pub data_dir: PathBuf, 43 + pub data: PathBuf, 34 44 #[serde(default)] 35 - pub config_dir: PathBuf, 45 + pub config: PathBuf, 36 46 } 37 47 38 48 /// Configuration for the App ··· 45 55 } 46 56 47 57 impl Config { 48 - pub fn new() -> Self { 49 - let default_config: KdlDocument = DEFAULT_CONFIG 58 + /// generates a new config with the provided `filaments_dir` 59 + pub fn generate(filaments_dir: &Path) -> KdlDocument { 60 + let mut default_config: KdlDocument = DEFAULT_CONFIG 50 61 .parse() 51 62 .expect("Default config should always be a valid KDL document."); 52 63 53 - let keymap_node = default_config 54 - .get("keymap") 55 - .expect("Config::new Keymap must exist in default config."); 64 + if let Some(node) = default_config 65 + .nodes_mut() 66 + .iter_mut() 67 + .find(|n| n.name().value() == "filaments_dir") 68 + && let Some(entry) = node.entries_mut().get_mut(0) 69 + { 70 + *entry.value_mut() = kdl::KdlValue::String(filaments_dir.to_string_lossy().to_string()); 71 + entry.clear_format(); 72 + } 73 + 74 + default_config 75 + } 76 + 77 + /// Parse the config from `~/.config/filametns` 78 + /// 79 + /// # Errors 80 + /// 81 + /// Will error if the config doesn't exist or if there 82 + /// is a problem parsing it. 83 + pub fn parse() -> color_eyre::Result<Self> { 84 + let config: KdlDocument = { 85 + let file_path = get_config_dir().join("config.kdl"); 86 + 87 + let mut file = File::open(file_path).context("Failed to find file!")?; 88 + 89 + let mut str = String::new(); 90 + 91 + file.read_to_string(&mut str) 92 + .context("Failed to read file!")?; 56 93 57 - let keymap = 58 - KeyMap::try_from(keymap_node).expect("default config should always be a valid keymap"); 94 + str.parse().context("Expected to be valid kdl")? 95 + }; 96 + 97 + let keymap = KeyMap::try_from( 98 + config 99 + .get("keymap") 100 + .expect("Keymap must exist in the config"), 101 + ) 102 + .context("Keymap is not valid!")?; 59 103 60 - Self { 104 + let filaments_dir_str = config 105 + .get("filaments_dir") 106 + .expect("config should always have this specified") 107 + .get(0) 108 + .and_then(|arg| arg.as_string()) 109 + .expect("filaments_dir must be a string"); 110 + 111 + let filaments_dir = PathBuf::from(filaments_dir_str) 112 + .canonicalize() 113 + .context("Filaments directory does not exist!")?; 114 + 115 + Ok(Self { 61 116 app_config: AppConfig { 62 - data_dir: get_data_dir(), 63 - config_dir: get_config_dir(), 117 + filaments: filaments_dir, 118 + data: get_data_dir(), 119 + config: get_config_dir(), 64 120 }, 65 121 keymap, 66 - } 122 + }) 67 123 } 68 124 } 69 125 ··· 80 136 /// Returns the path to the OS-agnostic config directory. 81 137 pub fn get_config_dir() -> PathBuf { 82 138 CONFIG_DIRECTORY.clone().unwrap_or_else(|| { 83 - project_directory().map_or_else( 139 + home_dir().map_or_else( 84 140 || PathBuf::from(".").join(".config"), 85 - |proj_dirs| proj_dirs.config_local_dir().to_path_buf(), 141 + |mut path| { 142 + path.push(".config"); 143 + path.push("filaments"); 144 + path 145 + }, 86 146 ) 87 147 }) 88 148 } 89 - 90 149 fn project_directory() -> Option<ProjectDirs> { 91 150 ProjectDirs::from("com", "suri-codes", env!("CARGO_PKG_NAME")) 92 151 }
+7 -11
src/main.rs
··· 4 4 5 5 use crate::{app::App, cli::Cli}; 6 6 use clap::Parser; 7 - use db::Db; 8 7 9 8 mod app; 10 9 mod cli; ··· 23 22 24 23 let args = Cli::parse(); 25 24 26 - let _db = Db::connect("/tmp/filaments/test_db.sqlite").await?; 27 - 28 25 // if there is any subcommand, we want to execute that, otherwise we 29 26 // just run the app 27 + if let Some(command) = args.command { 28 + return command.process(); 29 + } 30 30 31 - if let Some(command) = args.command { 32 - match command { 33 - cli::Commands::Test => {} 34 - } 35 - } else { 36 - let mut app = App::new(args.tick_rate, args.frame_rate); 31 + // if no command we run the app 32 + 33 + let mut app = App::new(args.tick_rate, args.frame_rate)?; 34 + app.run().await?; 37 35 38 - app.run().await?; 39 - } 40 36 Ok(()) 41 37 }