Move config (de)serialization to separate file

This commit is contained in:
Henrik Friedrichsen
2021-04-11 00:25:02 +02:00
parent 8c68347c24
commit e61e18608b
3 changed files with 77 additions and 53 deletions

View File

@@ -1,5 +1,5 @@
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::path::PathBuf;
use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard};
use std::{fs, process};
@@ -9,6 +9,7 @@ use platform_dirs::AppDirs;
use crate::command::{SortDirection, SortKey};
use crate::playable::Playable;
use crate::queue;
use crate::serialization::{Serializer, TOML};
pub const CLIENT_ID: &str = "d420a117a32841c2b3474932e49fb54b";
@@ -109,7 +110,7 @@ impl Config {
let mut userstate = {
let path = config_path("userstate.toml");
load_or_generate_default(path, |_| Ok(UserState::default()), true)
TOML.load_or_generate_default(path, || Ok(UserState::default()), true)
.expect("could not load user state")
};
@@ -146,7 +147,7 @@ impl Config {
pub fn save_state(&self) {
let path = config_path("userstate.toml");
debug!("saving user state to {}", path.display());
if let Err(e) = write_content_helper(path, self.state().clone()) {
if let Err(e) = TOML.write(path, self.state().clone()) {
error!("Could not save user state: {}", e);
}
}
@@ -164,7 +165,7 @@ impl Config {
fn load() -> Result<ConfigValues, String> {
let path = config_path("config.toml");
load_or_generate_default(path, |_| Ok(ConfigValues::default()), false)
TOML.load_or_generate_default(path, || Ok(ConfigValues::default()), false)
}
fn proj_dirs() -> AppDirs {
@@ -203,52 +204,3 @@ pub fn cache_path(file: &str) -> PathBuf {
pb.push(file);
pb
}
/// Configuration and credential file helper
/// Creates a default configuration if none exist, otherwise will optionally overwrite
/// the file if it fails to parse
pub fn load_or_generate_default<
P: AsRef<Path>,
T: serde::Serialize + serde::de::DeserializeOwned,
F: Fn(&Path) -> Result<T, String>,
>(
path: P,
default: F,
default_on_parse_failure: bool,
) -> Result<T, String> {
let path = path.as_ref();
// Nothing exists so just write the default and return it
if !path.exists() {
let value = default(&path)?;
return write_content_helper(&path, value);
}
// load the serialized content. Always report this failure
let contents = std::fs::read_to_string(&path)
.map_err(|e| format!("Unable to read {}: {}", path.to_string_lossy(), e))?;
// Deserialize the content, optionally fall back to default if it fails
let result = toml::from_str(&contents);
if default_on_parse_failure && result.is_err() {
let value = default(&path)?;
return write_content_helper(&path, value);
}
result.map_err(|e| format!("Unable to parse {}: {}", path.to_string_lossy(), e))
}
fn write_content_helper<P: AsRef<Path>, T: serde::Serialize>(
path: P,
value: T,
) -> Result<T, String> {
let content =
toml::to_string_pretty(&value).map_err(|e| format!("Failed serializing value: {}", e))?;
fs::write(path.as_ref(), content)
.map(|_| value)
.map_err(|e| {
format!(
"Failed writing content to {}: {}",
path.as_ref().display(),
e
)
})
}

View File

@@ -60,6 +60,7 @@ mod library;
mod playable;
mod playlist;
mod queue;
mod serialization;
mod sharing;
mod show;
mod spotify;

71
src/serialization.rs Normal file
View File

@@ -0,0 +1,71 @@
use std::{fs, path::Path};
pub trait Serializer {
/// Configuration and credential file helper
/// Creates a default configuration if none exist, otherwise will optionally overwrite
/// the file if it fails to parse
fn load_or_generate_default<
P: AsRef<Path>,
T: serde::Serialize + serde::de::DeserializeOwned,
F: Fn() -> Result<T, String>,
>(
&self,
path: P,
default: F,
default_on_parse_failure: bool,
) -> Result<T, String> {
let path = path.as_ref();
// Nothing exists so just write the default and return it
if !path.exists() {
let value = default()?;
return self.write(&path, value);
}
let result = self.load(&path);
if default_on_parse_failure && result.is_err() {
let value = default()?;
return self.write(&path, value);
}
result.map_err(|e| format!("Unable to parse {}: {}", path.to_string_lossy(), e))
}
fn load<P: AsRef<Path>, T: serde::Serialize + serde::de::DeserializeOwned>(
&self,
path: P,
) -> Result<T, String>;
fn write<P: AsRef<Path>, T: serde::Serialize>(&self, path: P, value: T) -> Result<T, String>;
}
pub struct TomlSerializer {}
impl Serializer for TomlSerializer {
fn load<P: AsRef<Path>, T: serde::Serialize + serde::de::DeserializeOwned>(
&self,
path: P,
) -> Result<T, String> {
let contents = std::fs::read_to_string(&path)
.map_err(|e| format!("Unable to read {}: {}", path.as_ref().to_string_lossy(), e))?;
toml::from_str(&contents).map_err(|e| {
format!(
"Unable to parse toml {}: {}",
path.as_ref().to_string_lossy(),
e
)
})
}
fn write<P: AsRef<Path>, T: serde::Serialize>(&self, path: P, value: T) -> Result<T, String> {
let content = toml::to_string_pretty(&value)
.map_err(|e| format!("Failed serializing value: {}", e))?;
fs::write(path.as_ref(), content)
.map(|_| value)
.map_err(|e| {
format!(
"Failed writing content to {}: {}",
path.as_ref().display(),
e
)
})
}
}
pub static TOML: TomlSerializer = TomlSerializer {};