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);
+ }
+ }
}