config.rs
1 use std::{fs, io::stdin, path::PathBuf, sync::LazyLock}; 2 3 use anyhow::{Context as _, Result, bail, ensure}; 4 5 use crate::{add::bool_question, util::home}; 6 7 #[expect(clippy::unwrap_used)] 8 pub static CONFIG: LazyLock<Config> = LazyLock::new(|| Config::load().unwrap()); 9 10 #[derive(Default)] 11 pub struct Config { 12 /// The default subdir of files/ 13 pub default_subdir: String, 14 /// The path to the files/ directory 15 pub files_path: String, 16 /// The paths that should be searched by `list()` 17 pub list_paths: Vec<String>, 18 /// The paths that shouldn't be searched by `list()` 19 pub ignore_paths: Vec<PathBuf>, 20 /// Whether to run 'list' with root privileges 21 pub root: bool, 22 } 23 impl Config { 24 pub fn setup() -> Result<()> { 25 let stdin = stdin(); 26 27 let mut file_string = String::new(); 28 29 let mut prompt_field = |field: &str, prompt: &str| -> Result<()> { 30 file_string.push_str(field); 31 file_string.push_str(" = "); 32 println!("{prompt}:"); 33 stdin.read_line(&mut file_string)?; 34 Ok(()) 35 }; 36 37 prompt_field( 38 "files_path", 39 "Please enter the path of the files that should be managed by dots", 40 )?; 41 42 prompt_field("default_subdir", "Please enter the default subdir")?; 43 44 prompt_field( 45 "list_paths", 46 "Please enter the paths that should be searched by `dots list`, separated by commas", 47 )?; 48 49 prompt_field( 50 "ignore_paths", 51 "Please enter the paths that should be ignored by `dots list`, separated by commas", 52 )?; 53 54 if bool_question("Should `dots list` be run with root privileges?")? { 55 file_string.push_str("root"); 56 } 57 58 let path = format!("{}/.config/dots", home()?); 59 60 fs::write(path, file_string)?; 61 62 Ok(()) 63 } 64 fn load() -> Result<Self> { 65 let path = format!("{}/.config/dots", home()?); 66 67 let string = fs::read_to_string(&path) 68 .with_context(|| format!("\nFailed to read config at {path}. Run `dots config` to create it interactively or do so manually."))?; 69 70 let mut config = Self::default(); 71 72 for line in string.lines() { 73 match line.split_once('=') { 74 Some((key, value)) => match key.trim() { 75 "default_subdir" => value.trim().clone_into(&mut config.default_subdir), 76 "files_path" => value.trim().clone_into(&mut config.files_path), 77 "list_paths" => config 78 .list_paths 79 .extend(value.split(',').map(|value| value.trim().to_owned())), 80 "ignore_paths" => config 81 .ignore_paths 82 .extend(value.split(',').map(|value| value.trim().into())), 83 "root" => config.root = true, 84 other => bail!("Unknown config entry: {other}"), 85 }, 86 None => match line.trim() { 87 "root" => config.root = true, 88 other => bail!("Unknown config key: {other}"), 89 }, 90 } 91 } 92 93 ensure!( 94 !config.default_subdir.is_empty(), 95 "default_subdir is empty or not in the config. Maybe try adding something like `default_subdir = common` to your dots config file" 96 ); 97 98 Ok(config) 99 } 100 }