dkdc_links/
config.rs

1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Debug, Serialize, Deserialize, Default)]
8pub struct Config {
9    #[serde(default)]
10    pub aliases: HashMap<String, String>,
11    #[serde(default)]
12    pub links: HashMap<String, String>,
13}
14
15const DEFAULT_CONFIG: &str = r#"# dkdc-links config file
16[aliases]
17alias1 = "link1"
18a1 = "link1"
19alias2 = "link2"
20a2 = "link2"
21[links]
22link1 = "https://crates.io/crates/dkdc-links"
23link2 = "https://github.com/lostmygithubaccount/dkdc-links"
24"#;
25
26pub fn config_path() -> Result<PathBuf> {
27    let home_dir = std::env::var("HOME").context("Failed to get HOME environment variable")?;
28    Ok(PathBuf::from(home_dir)
29        .join(".config")
30        .join("dkdc")
31        .join("links")
32        .join("config.toml"))
33}
34
35pub fn init_config() -> Result<()> {
36    let config_path = config_path()?;
37
38    if !config_path.exists() {
39        let config_dir = config_path.parent().unwrap();
40        fs::create_dir_all(config_dir).context("Failed to create config directory")?;
41        fs::write(&config_path, DEFAULT_CONFIG).context("Failed to write default config")?;
42    }
43
44    Ok(())
45}
46
47pub fn load_config() -> Result<Config> {
48    let config_path = config_path()?;
49    let contents = fs::read_to_string(&config_path).context("Failed to read config file")?;
50    let config: Config = toml::from_str(&contents).context("Failed to parse config file")?;
51    Ok(config)
52}
53
54pub fn config_it() -> Result<()> {
55    let config_path = config_path()?;
56    let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
57
58    println!("Opening {} with {}...", config_path.display(), editor);
59
60    let status = std::process::Command::new(&editor)
61        .arg(&config_path)
62        .status()
63        .with_context(|| format!("Editor {editor} not found in PATH"))?;
64
65    if !status.success() {
66        anyhow::bail!("Editor exited with non-zero status");
67    }
68
69    Ok(())
70}
71
72pub fn print_config(config: &Config) -> Result<()> {
73    if !config.aliases.is_empty() {
74        println!("aliases:");
75        println!();
76
77        let mut keys: Vec<_> = config.aliases.keys().collect();
78        keys.sort_unstable();
79
80        let max_key_len = keys.iter().map(|k| k.len()).max().unwrap_or(0);
81
82        for key in keys {
83            if let Some(value) = config.aliases.get(key) {
84                println!("• {key:<max_key_len$} | {value}");
85            }
86        }
87
88        println!();
89    }
90
91    if !config.links.is_empty() {
92        println!("links:");
93        println!();
94
95        let mut keys: Vec<_> = config.links.keys().collect();
96        keys.sort_unstable();
97
98        let max_key_len = keys.iter().map(|k| k.len()).max().unwrap_or(0);
99
100        for key in keys {
101            if let Some(value) = config.links.get(key) {
102                println!("• {key:<max_key_len$} | {value}");
103            }
104        }
105
106        println!();
107    }
108
109    Ok(())
110}