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}