use camino::Utf8PathBuf;
use figment::{
Metadata, Profile, Provider,
error::Kind,
value::{Dict, Value as FigmentValue},
};
use serde_saphyr::Options;
use super::helpers::open_parent_dir_and_name;
#[derive(Debug, Clone)]
enum YamlInput {
File,
Inline(String),
}
#[derive(Debug, Clone)]
pub struct SaphyrYaml {
path: Utf8PathBuf,
input: YamlInput,
profile: Option<Profile>,
}
impl SaphyrYaml {
#[must_use]
pub fn file<P: Into<Utf8PathBuf>>(path: P) -> Self {
Self {
path: path.into(),
input: YamlInput::File,
profile: None,
}
}
#[must_use]
pub fn string<P, S>(path: P, contents: S) -> Self
where
P: Into<Utf8PathBuf>,
S: Into<String>,
{
Self {
path: path.into(),
input: YamlInput::Inline(contents.into()),
profile: None,
}
}
#[must_use]
pub fn profile<P: Into<Profile>>(mut self, profile: P) -> Self {
self.profile = Some(profile.into());
self
}
fn read_contents(&self) -> std::io::Result<String> {
match &self.input {
YamlInput::File => {
let (dir, file_name) = open_parent_dir_and_name(&self.path)?;
dir.read_to_string(file_name)
}
YamlInput::Inline(contents) => Ok(contents.clone()),
}
}
fn parse_value(contents: &str) -> Result<FigmentValue, serde_saphyr::Error> {
serde_saphyr::from_str_with_options(
contents,
Options {
strict_booleans: true,
..Options::default()
},
)
}
}
impl Provider for SaphyrYaml {
fn metadata(&self) -> Metadata {
Metadata::from("Saphyr YAML", self.path.as_std_path())
}
fn data(&self) -> Result<std::collections::BTreeMap<Profile, Dict>, figment::Error> {
let contents = self
.read_contents()
.map_err(|err| figment::Error::from(format!("failed to read {}: {err}", self.path)))?;
let value = Self::parse_value(&contents).map_err(|err| {
figment::Error::from(Kind::Message(format!(
"failed to parse {}: {err}",
self.path
)))
})?;
let actual = value.to_actual();
let dict = value
.into_dict()
.ok_or_else(|| figment::Error::from(Kind::InvalidType(actual, "map".into())))?;
let profile = self.profile.clone().unwrap_or(Profile::Default);
Ok(profile.collect(dict))
}
}