use std::path::{Path, PathBuf};
use figment::providers::Format;
use figment::value::{Dict, Map};
use figment::{Error as FigmentError, Metadata, Profile, Provider};
use crate::ParserConfig;
#[derive(Debug, Clone, Copy)]
pub struct Yaml;
impl Format for Yaml {
type Error = FigmentError;
const NAME: &'static str = "YAML";
fn from_str<T: serde_core::de::DeserializeOwned>(s: &str) -> Result<T, Self::Error> {
parse(s, &ParserConfig::default()).map_err(to_figment)
}
fn from_path<T: serde_core::de::DeserializeOwned>(path: &Path) -> Result<T, Self::Error> {
let config = ParserConfig::default();
read_bounded(path, &config)
.and_then(|text| parse(&text, &config))
.map_err(to_figment)
}
}
impl Yaml {
#[must_use]
pub fn string_with_config(yaml: &str, config: ParserConfig) -> YamlWithConfig {
YamlWithConfig::new(Source::String(yaml.to_owned()), config)
}
#[must_use]
pub fn file_with_config<P: AsRef<Path>>(path: P, config: ParserConfig) -> YamlWithConfig {
YamlWithConfig::new(Source::File(path.as_ref().to_path_buf()), config)
}
}
#[derive(Debug, Clone)]
pub struct YamlWithConfig {
source: Source,
config: ParserConfig,
profile: Option<Profile>,
}
#[derive(Debug, Clone)]
enum Source {
String(String),
File(PathBuf),
}
impl YamlWithConfig {
fn new(source: Source, config: ParserConfig) -> Self {
Self {
source,
config,
profile: Some(Profile::Default),
}
}
#[must_use]
pub fn nested(mut self) -> Self {
self.profile = None;
self
}
#[must_use]
pub fn profile<P: Into<Profile>>(mut self, profile: P) -> Self {
self.profile = Some(profile.into());
self
}
}
impl Provider for YamlWithConfig {
fn metadata(&self) -> Metadata {
match &self.source {
Source::String(_) => Metadata::named("YAML source string"),
Source::File(path) => Metadata::from("YAML file", path.as_path()),
}
}
fn data(&self) -> Result<Map<Profile, Dict>, FigmentError> {
let file_text;
let text = match &self.source {
Source::String(s) => s.as_str(),
Source::File(path) => {
file_text = read_bounded(path, &self.config).map_err(to_figment)?;
file_text.as_str()
}
};
match &self.profile {
Some(profile) => parse::<Dict>(text, &self.config).map(|dict| profile.collect(dict)),
None => parse(text, &self.config),
}
.map_err(to_figment)
}
}
fn parse<T: serde_core::de::DeserializeOwned>(s: &str, config: &ParserConfig) -> crate::Result<T> {
crate::de::from_str_typed_no_tag_preserve::<T>(s, config)
}
fn read_bounded(path: &Path, config: &ParserConfig) -> crate::Result<String> {
let file = std::fs::File::open(path).map_err(crate::Error::Io)?;
crate::de::read_to_string_bounded(file, config)
}
fn to_figment(error: crate::Error) -> FigmentError {
FigmentError::from(error.to_string())
}