main
1use anyhow::{Context, Result};
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::path::PathBuf;
6
7#[derive(Debug, Deserialize, Serialize)]
8pub struct Config {
9 pub feeds: HashMap<String, String>,
10 pub radio: Option<HashMap<String, String>>,
11 pub music_dirs: Option<Vec<String>>,
12}
13
14impl Config {
15 pub fn load() -> Result<Self> {
16 let config_path = Self::config_path()?;
17
18 if !config_path.exists() {
19 // Create a default config
20 let default_config = Self::default();
21 default_config.save()?;
22 return Ok(default_config);
23 }
24
25 let content = fs::read_to_string(&config_path)
26 .with_context(|| format!("Failed to read config file: {:?}", config_path))?;
27
28 eprintln!("Loading config from: {:?}", config_path);
29 eprintln!(
30 "Config content preview: {}",
31 &content[0..content.len().min(200)]
32 );
33
34 let config: Config = serde_yaml::from_str(&content)
35 .with_context(|| format!("Failed to parse config file: {:?}", config_path))?;
36
37 eprintln!("Loaded {} feeds", config.feeds.len());
38 for (name, url) in &config.feeds {
39 eprintln!(" Feed: {} -> {}", name, url);
40 }
41
42 Ok(config)
43 }
44
45 pub fn save(&self) -> Result<()> {
46 let config_path = Self::config_path()?;
47
48 if let Some(parent) = config_path.parent() {
49 fs::create_dir_all(parent)
50 .with_context(|| format!("Failed to create config directory: {:?}", parent))?;
51 }
52
53 let content = serde_yaml::to_string(self).with_context(|| "Failed to serialize config")?;
54
55 fs::write(&config_path, content)
56 .with_context(|| format!("Failed to write config file: {:?}", config_path))?;
57
58 Ok(())
59 }
60
61 fn config_path() -> Result<PathBuf> {
62 // First check for XDG config directory (preferred)
63 if let Ok(xdg_config) = std::env::var("XDG_CONFIG_HOME") {
64 let path = PathBuf::from(xdg_config).join("ghetto-blaster.yml");
65 if path.exists() {
66 return Ok(path);
67 }
68 }
69
70 // Then check ~/.config/ghetto-blaster.yml (Unix standard)
71 if let Some(home_dir) = dirs::home_dir() {
72 let path = home_dir.join(".config").join("ghetto-blaster.yml");
73 if path.exists() {
74 return Ok(path);
75 }
76 }
77
78 // Fall back to platform-specific config directory
79 let config_dir = dirs::config_dir().with_context(|| "Failed to find config directory")?;
80
81 Ok(config_dir.join("ghetto-blaster.yml"))
82 }
83}
84
85impl Default for Config {
86 fn default() -> Self {
87 let mut feeds = HashMap::new();
88 feeds.insert(
89 "Rustacean Station".to_string(),
90 "https://rustacean-station.org/podcast.rss".to_string(),
91 );
92 feeds.insert(
93 "Syntax".to_string(),
94 "https://feeds.transistor.fm/syntax".to_string(),
95 );
96 feeds.insert(
97 "The Changelog".to_string(),
98 "https://changelog.com/podcast/feed".to_string(),
99 );
100
101 let mut radio = HashMap::new();
102 radio.insert(
103 "CBC Radio 1".to_string(),
104 "https://cbc-radio1-moncton.cast.addradio.de/cbc/radio1/moncton/mp3/high".to_string(),
105 );
106
107 Self {
108 feeds,
109 radio: Some(radio),
110 music_dirs: Some(vec!["~/Music".to_string()]),
111 }
112 }
113}