#![cfg_attr(doc_cfg, doc(cfg(feature = "config")))]
use serde::{de::DeserializeOwned, Serialize};
use std::path::Path;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[cfg(feature = "yaml")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "yaml")))]
#[error("config (de)serialisation to YAML failed")]
Yaml(#[from] serde_yaml::Error),
#[cfg(feature = "json")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "json")))]
#[error("config (de)serialisation to JSON failed")]
Json(#[from] serde_json::Error),
#[cfg(feature = "ron")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "ron")))]
#[error("config serialisation to RON failed")]
Ron(#[from] ron::Error),
#[cfg(feature = "ron")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "ron")))]
#[error("config deserialisation from RON failed")]
RonSpanned(#[from] ron::error::SpannedError),
#[error("error reading / writing config file")]
IoError(#[from] std::io::Error),
#[error("format not supported: {0}")]
UnsupportedFormat(Format),
}
#[non_exhaustive]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Error)]
pub enum Format {
#[error("no format")]
None,
#[error("JSON")]
Json,
#[error("TOML")]
Toml,
#[error("YAML")]
Yaml,
#[error("RON")]
Ron,
#[error("(unknown format)")]
Unknown,
}
impl Default for Format {
fn default() -> Self {
Format::None
}
}
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
}
}
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 r = std::io::BufReader::new(std::fs::File::open(path)?);
Ok(serde_yaml::from_reader(r)?)
}
#[cfg(feature = "ron")]
Format::Ron => {
let r = std::io::BufReader::new(std::fs::File::open(path)?);
Ok(ron::de::from_reader(r)?)
}
_ => {
let _ = path; Err(Error::UnsupportedFormat(self))
}
}
}
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 w = std::io::BufWriter::new(std::fs::File::create(path)?);
serde_json::to_writer_pretty(w, value)?;
Ok(())
}
#[cfg(feature = "yaml")]
Format::Yaml => {
let w = std::io::BufWriter::new(std::fs::File::create(path)?);
serde_yaml::to_writer(w, value)?;
Ok(())
}
#[cfg(feature = "ron")]
Format::Ron => {
let w = std::io::BufWriter::new(std::fs::File::create(path)?);
let pretty = ron::ser::PrettyConfig::default();
ron::ser::to_writer_pretty(w, value, pretty)?;
Ok(())
}
_ => {
let _ = (path, value); Err(Error::UnsupportedFormat(self))
}
}
}
#[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)
}
#[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)
}
}