My personal-knowledge-system, with deeply integrated task tracking and long term goal planning capabilities.
at main 150 lines 4.4 kB view raw
1use color_eyre::eyre::Context; 2use directories::ProjectDirs; 3use kdl::KdlDocument; 4use serde::Deserialize; 5use std::{ 6 env::{self, home_dir}, 7 fmt::Debug, 8 fs::File, 9 io::Read, 10 path::{Path, PathBuf}, 11 sync::LazyLock, 12}; 13 14use crate::tui::KeyMap; 15 16/// Project Name: Filaments 17pub static PROJECT_NAME: LazyLock<String> = 18 LazyLock::new(|| env!("CARGO_CRATE_NAME").to_uppercase()); 19 20/// The OS-agnostic data directory for the project. 21pub static DATA_DIRECTORY: LazyLock<Option<PathBuf>> = LazyLock::new(|| { 22 env::var(format!("{}_DATA", PROJECT_NAME.clone())) 23 .ok() 24 .map(PathBuf::from) 25}); 26 27/// The OS-agnostic config directory for the project. 28pub static CONFIG_DIRECTORY: LazyLock<Option<PathBuf>> = LazyLock::new(|| { 29 env::var(format!("{}_CONFIG", PROJECT_NAME.clone())) 30 .ok() 31 .map(PathBuf::from) 32}); 33 34const DEFAULT_CONFIG: &str = include_str!("../.config/config.kdl"); 35 36/// The App Config and Data locations. 37#[derive(Clone, Debug, Deserialize, Default)] 38#[expect(dead_code)] 39pub struct AppConfig { 40 /// The directory where the single instance of the filaments exists. 41 pub workspace: PathBuf, 42 #[serde(default)] 43 pub data: PathBuf, 44 #[serde(default)] 45 pub config: PathBuf, 46} 47 48/// Configuration for the App 49#[derive(Debug, Clone)] 50pub struct Config { 51 pub app_config: AppConfig, 52 pub keymap: KeyMap, 53 // pub styles: Styles, 54} 55 56impl Config { 57 /// generates a new config with the provided `filaments_dir` 58 pub fn generate(filaments_dir: &Path) -> KdlDocument { 59 let mut default_config: KdlDocument = DEFAULT_CONFIG 60 .parse() 61 .expect("Default config should always be a valid KDL document."); 62 63 if let Some(node) = default_config 64 .nodes_mut() 65 .iter_mut() 66 .find(|n| n.name().value() == "filaments_dir") 67 && let Some(entry) = node.entries_mut().get_mut(0) 68 { 69 *entry.value_mut() = kdl::KdlValue::String(filaments_dir.to_string_lossy().to_string()); 70 entry.clear_format(); 71 } 72 73 default_config 74 } 75 76 /// Parse the config from `~/.config/filametns` 77 /// 78 /// # Errors 79 /// 80 /// Will error if the config doesn't exist or if there 81 /// is a problem parsing it. 82 pub fn parse() -> color_eyre::Result<Self> { 83 let config: KdlDocument = { 84 let file_path = get_config_dir().join("config.kdl"); 85 86 let mut file = File::open(file_path).context("Failed to find file!")?; 87 88 let mut str = String::new(); 89 90 file.read_to_string(&mut str) 91 .context("Failed to read file!")?; 92 93 str.parse().context("Expected to be valid kdl")? 94 }; 95 96 let keymap = KeyMap::try_from( 97 config 98 .get("keymap") 99 .expect("Keymap must exist in the config"), 100 ) 101 .context("Keymap is not valid!")?; 102 103 let filaments_dir_str = config 104 .get("filaments_dir") 105 .expect("config should always have this specified") 106 .get(0) 107 .and_then(|arg| arg.as_string()) 108 .expect("filaments_dir must be a string"); 109 110 let filaments_dir = PathBuf::from(filaments_dir_str) 111 .canonicalize() 112 .context("Filaments directory does not exist!")?; 113 114 Ok(Self { 115 app_config: AppConfig { 116 workspace: filaments_dir, 117 data: get_data_dir(), 118 config: get_config_dir(), 119 }, 120 keymap, 121 }) 122 } 123} 124 125/// Returns the path to the OS-agnostic data directory. 126pub fn get_data_dir() -> PathBuf { 127 DATA_DIRECTORY.clone().unwrap_or_else(|| { 128 project_directory().map_or_else( 129 || PathBuf::from(".").join(".data"), 130 |proj_dirs| proj_dirs.data_local_dir().to_path_buf(), 131 ) 132 }) 133} 134 135/// Returns the path to the OS-agnostic config directory. 136pub fn get_config_dir() -> PathBuf { 137 CONFIG_DIRECTORY.clone().unwrap_or_else(|| { 138 home_dir().map_or_else( 139 || PathBuf::from(".").join(".config"), 140 |mut path| { 141 path.push(".config"); 142 path.push("filaments"); 143 path 144 }, 145 ) 146 }) 147} 148fn project_directory() -> Option<ProjectDirs> { 149 ProjectDirs::from("com", "suri-codes", env!("CARGO_PKG_NAME")) 150}