From 21d48c302236890332583d8433b8f67693039708 Mon Sep 17 00:00:00 2001 From: Robbert van der Helm Date: Mon, 13 Jul 2020 14:31:33 +0200 Subject: [PATCH] Implement reading and writing of config files --- tools/yabridgectl/Cargo.lock | 13 ++++++ tools/yabridgectl/Cargo.toml | 2 + tools/yabridgectl/src/config.rs | 70 +++++++++++++++++++++++++++++++-- tools/yabridgectl/src/main.rs | 17 +++++++- 4 files changed, 98 insertions(+), 4 deletions(-) diff --git a/tools/yabridgectl/Cargo.lock b/tools/yabridgectl/Cargo.lock index 7fc29473..755c06f8 100644 --- a/tools/yabridgectl/Cargo.lock +++ b/tools/yabridgectl/Cargo.lock @@ -290,6 +290,17 @@ version = "1.0.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5317f7588f0a5078ee60ef675ef96735a1442132dc645eb1d12c018620ed8cd3" +[[package]] +name = "serde_derive" +version = "1.0.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0be94b04690fbaed37cddffc5c134bf537c8e3329d53e982fe04c374978f8e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "strsim" version = "0.10.0" @@ -430,6 +441,8 @@ dependencies = [ "aho-corasick", "clap", "rayon", + "serde", + "serde_derive", "toml", "walkdir", "xdg", diff --git a/tools/yabridgectl/Cargo.toml b/tools/yabridgectl/Cargo.toml index c9afb3ad..5594950d 100644 --- a/tools/yabridgectl/Cargo.toml +++ b/tools/yabridgectl/Cargo.toml @@ -13,6 +13,8 @@ license = "GPL-3.0-or-later" aho-corasick = "0.7.13" clap = "3.0.0-beta.1" rayon = "1.3.1" +serde = "1.0.114" +serde_derive = "1.0.114" toml = "0.5.6" walkdir = "2.3.1" xdg = "2.2.0" diff --git a/tools/yabridgectl/src/config.rs b/tools/yabridgectl/src/config.rs index 6909e959..9a9a49e5 100644 --- a/tools/yabridgectl/src/config.rs +++ b/tools/yabridgectl/src/config.rs @@ -14,13 +14,21 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . +use serde_derive::{Deserialize, Serialize}; +use std::fs; use std::path::{Path, PathBuf}; use xdg::BaseDirectories; +/// The name of the config file, relative to `$XDG_CONFIG_HOME/CONFIG_PREFIX`. +const CONFIG_FILE_NAME: &str = "config.toml"; +/// The name of the configuration directory, relative to `$XDG_CONFIG_HOME`. +const CONFIG_PREFIX: &str = "yabridgectl"; + const LIBYABRIDGE_NAME: &str = "libyabridge.so"; /// The configuration used for yabridgectl. This will be serialized to and deserialized from /// `$XDG_CONFIG_HOME/yabridge/config.toml`. +#[derive(Deserialize, Serialize, Debug)] pub struct Config { /// The installation method to use. We will default to creating copies since that works /// everywehre. @@ -34,6 +42,55 @@ pub struct Config { } impl Config { + /// Try to read the config file, creating a new default file if necessary. This will fail if the + /// file could not be created or if it could not be parsed. + pub fn read() -> Result { + match base_directories()?.find_config_file(CONFIG_FILE_NAME) { + Some(path) => { + let toml_str = fs::read_to_string(&path).map_err(|err| { + format!( + "Could not read config file at '{}': {}", + path.display(), + err + ) + })?; + + Ok(toml::from_str(&toml_str) + .map_err(|err| format!("Could not parse TOML: {:#?}", err))?) + } + None => { + let defaults = Config { + method: InstallationMethod::Copy, + yabridge_home: None, + plugin_dirs: Vec::new(), + }; + + // If no existing config file exists, then write a new config file with default + // values + defaults.write()?; + + Ok(defaults) + } + } + } + + /// Write the config to disk, creating the file if it does not yet exist. + pub fn write(&self) -> Result<(), String> { + let toml_str = toml::to_string_pretty(&self) + .map_err(|err| format!("Could not format TOML: {}", err))?; + let config_file = base_directories()? + .place_config_file(CONFIG_FILE_NAME) + .map_err(|err| format!("Could not write config file: {}", err))?; + + fs::write(&config_file, toml_str).map_err(|err| { + format!( + "Could not write config file to '{}': {}", + config_file.display(), + err + ) + }) + } + /// Return the path to `libyabridge.so`, or a descriptive error if it can't be found. If /// `yabridge_home` is `None`, then we'll search in both `/usr/lib` and /// `$XDG_DATA_HOME/yabridge`. @@ -54,9 +111,7 @@ impl Config { None => { // Search in the two common installation locations if no path was set explicitely let system_path = Path::new("/usr/lib"); - let user_path = BaseDirectories::new() - .map_err(|err| format!("Error while parsing base directories:\n{}", err))? - .get_data_home(); + let user_path = base_directories()?.get_data_home(); for directory in &[system_path, &user_path] { let candidate = directory.join(LIBYABRIDGE_NAME); if candidate.exists() { @@ -77,6 +132,8 @@ impl Config { } /// Specifies how yabridge will be set up for the found plugins. +#[derive(Deserialize, Serialize, Debug)] +#[serde(rename_all = "snake_case")] pub enum InstallationMethod { /// Create a copy of `libyabridge.so` for every Windows VST2 plugin .dll file found. After /// updating yabridge, the user will have to rerun `yabridgectl sync` to copy over the new @@ -87,3 +144,10 @@ pub enum InstallationMethod { /// modify the `PATH` environment variable. Symlink, } + +/// Fetch the XDG base directories, converting any error messages if this somehow fails into a +/// printable string to reduce boiler plate. +fn base_directories() -> Result { + BaseDirectories::with_prefix(CONFIG_PREFIX) + .map_err(|err| format!("Error while parsing base directories: {}", err)) +} diff --git a/tools/yabridgectl/src/main.rs b/tools/yabridgectl/src/main.rs index 02a1189e..90faed50 100644 --- a/tools/yabridgectl/src/main.rs +++ b/tools/yabridgectl/src/main.rs @@ -16,6 +16,21 @@ mod config; +use config::Config; + fn main() { - println!("Hello, world!"); + // TODO: Remove debug + match Config::read() { + Ok(config) => { + println!("Read config:\n\n{:#?}\n", config); + println!( + "Searching for libyabridge.toml:\n\n{:?}", + config.libyabridge() + ); + } + Err(err) => { + eprintln!("Error while reading config:\n\n{}", err); + std::process::exit(1); + } + } }