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 let config_dir = config_path.parent().unwrap();
38
39 fs::create_dir_all(config_dir).context("Failed to create config directory")?;
40
41 if !config_path.exists() {
42 fs::write(&config_path, DEFAULT_CONFIG).context("Failed to write default config")?;
43 }
44
45 Ok(())
46}
47
48pub fn load_config() -> Result<Config> {
49 let config_path = config_path()?;
50 let contents = fs::read_to_string(&config_path).context("Failed to read config file")?;
51 let config: Config = toml::from_str(&contents).context("Failed to parse config file")?;
52 Ok(config)
53}
54
55pub fn config_it() -> Result<()> {
56 let config_path = config_path()?;
57 let editor = std::env::var("EDITOR").unwrap_or_else(|_| "vi".to_string());
58
59 println!("Opening {} with {}...", config_path.display(), editor);
60
61 let status = std::process::Command::new(&editor)
62 .arg(&config_path)
63 .status()
64 .with_context(|| format!("Editor {editor} not found in PATH"))?;
65
66 if !status.success() {
67 anyhow::bail!("Editor exited with non-zero status");
68 }
69
70 Ok(())
71}
72
73pub fn print_config(config: &Config) -> Result<()> {
74 let sections = vec![("aliases", &config.aliases), ("links", &config.links)];
75
76 for (section_name, section_data) in sections {
77 if section_data.is_empty() {
78 continue;
79 }
80
81 println!("{section_name}:");
82 println!();
83
84 let mut keys: Vec<_> = section_data.keys().collect();
85 keys.sort();
86
87 let max_key_len = keys.iter().map(|k| k.len()).max().unwrap_or(0);
88
89 for key in keys {
90 if let Some(value) = section_data.get(key) {
91 println!("• {key:<max_key_len$} | {value}");
92 }
93 }
94
95 println!();
96 }
97
98 Ok(())
99}