use serde::{Deserialize, Serialize}; use serde_json::{from_str, to_string}; use std::env; use std::fs::{File, create_dir_all, write}; use std::io::{Read, Result}; use std::path::PathBuf; const CONFIG_ENV_VARIABLE: &str = "LYRICLI_CONFIG_DIRECTORY"; const CONFIG_DEFAULT_LOCATION: &str = "XDG_CONFIG_HOME"; const CONFIG_FALLBACK_LOCATION: &str = ".config"; const CONFIG_SUBDIRECTORY: &str = "lyricli"; const CONFIG_FILENAME: &str = "lyricli.conf"; #[derive(Serialize, Deserialize, Debug)] pub struct Configuration { enabled_sources: Vec, } impl Configuration { pub fn new() -> Self { if let Some(configuration) = Configuration::read() { return configuration; } let configuration = Configuration::default(); // Write the defaults. Configuration::write(&configuration).ok(); configuration } // Public API pub fn is_enabled(&self, source_name: &String) -> bool { self.enabled_sources.contains(source_name) } pub fn enable_source(&mut self, source_name: &String) -> Result<()> { if !self.enabled_sources.contains(source_name) { self.enabled_sources.push(source_name.to_owned()) } Configuration::write(self) } pub fn disable_source(&mut self, source_name: &String) -> Result<()> { self.enabled_sources.retain(|source| source != source_name); Configuration::write(self) } // Helpers fn default() -> Configuration { Configuration { enabled_sources: vec![ "apple_music".to_string(), "spotify".to_string(), "swinsian".to_string(), "mpris".to_string(), ], } } fn read() -> Option { let config_file_path = Configuration::file_path(); let mut config_file = File::open(&config_file_path).ok()?; let mut config_contents = String::new(); config_file.read_to_string(&mut config_contents).ok()?; from_str(&config_contents).ok()? } fn write(configuration: &Configuration) -> Result<()> { let config_file_path = Configuration::file_path(); if let Ok(serialized_configuration) = to_string(&configuration) { write(&config_file_path, serialized_configuration)?; } Ok(()) } fn file_path() -> PathBuf { let config_directory = Configuration::directory(); create_dir_all(&config_directory).ok(); config_directory.join(CONFIG_FILENAME) } fn directory() -> PathBuf { match env::var(CONFIG_ENV_VARIABLE) { Ok(directory) => PathBuf::from(directory), Err(_) => match env::var(CONFIG_DEFAULT_LOCATION) { Ok(directory) => PathBuf::from(directory), Err(_) => match env::var("HOME") { Ok(directory) => PathBuf::from(directory).join(CONFIG_FALLBACK_LOCATION), Err(_) => panic!( "Could not find required directory, {CONFIG_ENV_VARIABLE} or {CONFIG_DEFAULT_LOCATION} should be set and readable." ), }, }, } .join(CONFIG_SUBDIRECTORY) } }