use std::borrow::Cow;
use serde::{de::DeserializeOwned, Deserialize};
use thiserror::Error;
use crate::{path::Path, Configuration, ConfigurationBuilder, Error, MissingValue};
#[derive(Debug, Default, Error)]
#[error("Found secret at path `{0}`")]
pub struct UnexpectedSecret(Path);
impl UnexpectedSecret {
#[must_use]
pub fn prepend(mut self, path_segment: impl Into<Cow<'static, str>>) -> Self {
self.0 .0.push(path_segment.into());
self
}
}
#[derive(Debug, Default, Deserialize)]
#[serde(bound = "T: DeserializeOwned")]
pub struct SecretBuilder<T: ConfigurationBuilder>(T);
impl<T: ConfigurationBuilder> SecretBuilder<T> {
#[must_use]
pub fn merge(self, other: Self) -> Self {
Self(self.0.merge(other.0))
}
pub fn try_build(self) -> Result<T::Target, Error> {
self.0.try_build()
}
pub fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
if self.0.contains_non_secret_data().unwrap_or(true) {
Err(UnexpectedSecret::default())
} else {
Ok(false)
}
}
}
#[derive(Debug, Deserialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
#[serde(transparent)]
pub struct SecretOption<T>(Option<T>);
impl<T> Default for SecretOption<T> {
fn default() -> Self {
Self(None)
}
}
impl<T> ConfigurationBuilder for SecretOption<T>
where
T: serde::de::DeserializeOwned + Configuration,
{
type Target = T;
fn merge(self, other: Self) -> Self {
Self(self.0.or(other.0))
}
fn try_build(self) -> Result<Self::Target, Error> {
self.0
.ok_or_else(|| Error::MissingValue(MissingValue::default()))
}
fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
match self.0 {
Some(_) => Err(UnexpectedSecret::default()),
None => Ok(false),
}
}
}