use std::env; use std::fs::{create_dir_all, write, File}; use std::io::{Read, Result}; use std::path::PathBuf; use serde::{Deserialize, Serialize}; use serde_json; 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 = ".config"; 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() ] } } 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()?; serde_json::from_str(&config_contents).ok()? } fn write(configuration: &Configuration) -> Result<()> { let config_file_path = Configuration::file_path(); if let Ok(serialized_configuration) = serde_json::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, {} or {} should be set and readable.", CONFIG_ENV_VARIABLE, CONFIG_DEFAULT_LOCATION), }, }, }.join(CONFIG_SUBDIRECTORY) } }