#[cfg(feature = "serde")]
use serde::{Serialize, de::DeserializeOwned};
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[cfg(feature = "yaml")]
#[error("config deserialisation failed")]
De(#[from] serde::de::value::Error),
#[cfg(feature = "yaml")]
#[error("config serialisation to YAML failed")]
YamlSer(#[from] serde_yaml2::ser::Errors),
#[cfg(feature = "json")]
#[error("config (de)serialisation to JSON failed")]
Json(#[from] serde_json::Error),
#[cfg(feature = "ron")]
#[error("config serialisation to RON failed")]
Ron(#[from] ron::Error),
#[cfg(feature = "ron")]
#[error("config deserialisation from RON failed")]
RonSpanned(#[from] ron::error::SpannedError),
#[cfg(feature = "toml")]
#[error("config deserialisation from TOML failed")]
TomlDe(#[from] toml::de::Error),
#[cfg(feature = "toml")]
#[error("config serialisation to TOML failed")]
TomlSer(#[from] toml::ser::Error),
#[error("error reading / writing config file")]
IoError(#[from] std::io::Error),
#[error("format not supported: {0}")]
UnsupportedFormat(Format),
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Error)]
pub enum Format {
#[default]
#[error("no format")]
None,
#[error("JSON")]
Json,
#[error("TOML")]
Toml,
#[error("YAML")]
Yaml,
#[error("RON")]
Ron,
#[error("(unknown format)")]
Unknown,
}
impl Format {
pub fn guess_from_path(path: &Path) -> Format {
if let Some(ext) = path.extension() {
if ext == "json" {
Format::Json
} else if ext == "toml" {
Format::Toml
} else if ext == "yaml" {
Format::Yaml
} else if ext == "ron" {
Format::Ron
} else {
Format::Unknown
}
} else {
Format::Unknown
}
}
#[cfg(feature = "serde")]
pub fn read_path<T: DeserializeOwned>(self, path: &Path) -> Result<T, Error> {
log::info!("read_path: path={}, format={:?}", path.display(), self);
match self {
#[cfg(feature = "json")]
Format::Json => {
let r = std::io::BufReader::new(std::fs::File::open(path)?);
Ok(serde_json::from_reader(r)?)
}
#[cfg(feature = "yaml")]
Format::Yaml => {
let contents = std::fs::read_to_string(path)?;
Ok(serde_yaml2::from_str(&contents)?)
}
#[cfg(feature = "ron")]
Format::Ron => {
let r = std::io::BufReader::new(std::fs::File::open(path)?);
Ok(ron::de::from_reader(r)?)
}
#[cfg(feature = "toml")]
Format::Toml => {
let contents = std::fs::read_to_string(path)?;
Ok(toml::from_str(&contents)?)
}
_ => {
let _ = path; Err(Error::UnsupportedFormat(self))
}
}
}
#[cfg(feature = "serde")]
pub fn write_path<T: Serialize>(self, path: &Path, value: &T) -> Result<(), Error> {
log::info!("write_path: path={}, format={:?}", path.display(), self);
match self {
#[cfg(feature = "json")]
Format::Json => {
let text = serde_json::to_string_pretty(value)?;
std::fs::write(path, &text)?;
Ok(())
}
#[cfg(feature = "yaml")]
Format::Yaml => {
let text = serde_yaml2::to_string(value)?;
std::fs::write(path, text)?;
Ok(())
}
#[cfg(feature = "ron")]
Format::Ron => {
let pretty = ron::ser::PrettyConfig::default();
let text = ron::ser::to_string_pretty(value, pretty)?;
std::fs::write(path, &text)?;
Ok(())
}
#[cfg(feature = "toml")]
Format::Toml => {
let content = toml::to_string(value)?;
std::fs::write(path, &content)?;
Ok(())
}
_ => {
let _ = (path, value); Err(Error::UnsupportedFormat(self))
}
}
}
#[cfg(feature = "serde")]
#[inline]
pub fn guess_and_read_path<T: DeserializeOwned>(path: &Path) -> Result<T, Error> {
let format = Self::guess_from_path(path);
format.read_path(path)
}
#[cfg(feature = "serde")]
#[inline]
pub fn guess_and_write_path<T: Serialize>(path: &Path, value: &T) -> Result<(), Error> {
let format = Self::guess_from_path(path);
format.write_path(path, value)
}
}