1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3use std::fs;
4use std::path::PathBuf;
5
6#[derive(Debug, Serialize, Deserialize, Clone, Default)]
7pub struct PanexConfig {
8 #[serde(default)]
9 pub favorites: FavoritesConfig,
10 #[serde(default)]
11 pub open: OpenConfig,
12}
13
14#[derive(Debug, Serialize, Deserialize, Clone, Default)]
15pub struct FavoritesConfig {
16 #[serde(default)]
17 pub paths: Vec<String>,
18}
19
20#[derive(Debug, Serialize, Deserialize, Clone, Default)]
21pub struct OpenConfig {
22 #[serde(default)]
23 pub gui: HashMap<String, String>,
24 #[serde(default)]
25 pub tui: HashMap<String, String>,
26}
27
28impl PanexConfig {
29 pub fn config_path() -> Result<PathBuf, String> {
31 let home = dirs::home_dir().ok_or("Could not determine home directory")?;
32 Ok(home.join(".panex").join("config.toml"))
33 }
34
35 pub fn load() -> Self {
37 let path = match Self::config_path() {
38 Ok(p) => p,
39 Err(_) => return Self::default(),
40 };
41 let content = match fs::read_to_string(&path) {
42 Ok(c) => c,
43 Err(_) => return Self::default(),
44 };
45 toml::from_str(&content).unwrap_or_default()
46 }
47
48 pub fn save(&self) -> Result<(), String> {
50 let path = Self::config_path()?;
51 if let Some(parent) = path.parent() {
52 fs::create_dir_all(parent)
53 .map_err(|e| format!("Failed to create config directory: {}", e))?;
54 }
55 let content =
56 toml::to_string_pretty(self).map_err(|e| format!("Failed to serialize config: {}", e))?;
57 fs::write(&path, content).map_err(|e| format!("Failed to write config: {}", e))
58 }
59
60 pub fn is_favorite(&self, path: &str) -> bool {
61 let normalized = normalize_path(path);
62 self.favorites.paths.iter().any(|p| normalize_path(p) == normalized)
63 }
64
65 pub fn add_favorite(&mut self, path: &str) -> Result<(), String> {
66 let normalized = normalize_path(path);
67 if !self.is_favorite(&normalized) {
68 self.favorites.paths.push(normalized);
69 self.save()?;
70 }
71 Ok(())
72 }
73
74 pub fn remove_favorite(&mut self, path: &str) -> Result<(), String> {
75 let normalized = normalize_path(path);
76 self.favorites.paths.retain(|p| normalize_path(p) != normalized);
77 self.save()
78 }
79
80 pub fn toggle_favorite(&mut self, path: &str) -> Result<bool, String> {
81 if self.is_favorite(path) {
82 self.remove_favorite(path)?;
83 Ok(false)
84 } else {
85 self.add_favorite(path)?;
86 Ok(true)
87 }
88 }
89
90 pub fn get_gui_app(&self, ext: &str) -> Option<&String> {
92 let key = if ext.starts_with('.') {
93 ext.to_string()
94 } else {
95 format!(".{}", ext)
96 };
97 self.open.gui.get(&key)
98 }
99
100 pub fn get_tui_app(&self, ext: &str) -> Option<&String> {
102 let key = if ext.starts_with('.') {
103 ext.to_string()
104 } else {
105 format!(".{}", ext)
106 };
107 self.open.tui.get(&key)
108 }
109}
110
111fn normalize_path(path: &str) -> String {
113 let expanded = if path.starts_with('~') {
114 if let Some(home) = dirs::home_dir() {
115 path.replacen('~', &home.to_string_lossy(), 1)
116 } else {
117 path.to_string()
118 }
119 } else {
120 path.to_string()
121 };
122 expanded.trim_end_matches('/').to_string()
123}