use std::{fmt::Display, marker::PhantomData};
use serde::{de::DeserializeOwned, Deserialize};
use crate::{Configuration, ConfigurationBuilder, Error, MissingValue, UnexpectedSecret};
pub type BuilderOf<T> = <T as Configuration>::Builder;
pub type ItemOf<C> = <C as IntoIterator>::Item;
pub type KeyOf<C> = <C as KeyedContainer>::Key;
pub type TargetOf<B> = <B as ConfigurationBuilder>::Target;
pub type ValueOf<C> = <C as KeyedContainer>::Value;
#[derive(Debug, Default, Deserialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
#[serde(from = "Container")]
pub enum UnkeyedContainerBuilder<Container, Target> {
#[default]
Unspecified,
Some(Container),
_PhantomData(PhantomData<fn() -> Target>),
}
impl<Container, Target> From<Container> for UnkeyedContainerBuilder<Container, Target> {
fn from(value: Container) -> Self {
Self::Some(value)
}
}
impl<Container, Target> ConfigurationBuilder for UnkeyedContainerBuilder<Container, Target>
where
Self: DeserializeOwned,
Container: IntoIterator + 'static,
ItemOf<Container>: ConfigurationBuilder,
Target: Default + FromIterator<TargetOf<ItemOf<Container>>>,
for<'a> &'a Container: IntoIterator<Item = &'a ItemOf<Container>>,
{
type Target = Target;
fn merge(self, other: Self) -> Self {
if matches!(self, Self::Unspecified) {
other
} else {
self
}
}
fn try_build(self) -> Result<Self::Target, Error> {
match self {
Self::Unspecified => Err(Error::MissingValue(MissingValue::default())),
Self::Some(val) => val
.into_iter()
.map(ConfigurationBuilder::try_build)
.collect(),
Self::_PhantomData(_) => unreachable!("PhantomData is never instantiated"),
}
}
fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
match self {
Self::Unspecified => Ok(false),
Self::Some(val) => val
.into_iter()
.map(ConfigurationBuilder::contains_non_secret_data)
.enumerate()
.find(|(_index, result)| result.is_err())
.map(|(index, result)| result.map_err(|err| err.prepend(index.to_string())))
.unwrap_or(Ok(true)),
Self::_PhantomData(_) => unreachable!("PhantomData is never instantiated"),
}
}
}
pub trait KeyedContainer {
type Key;
type Value;
fn insert(&mut self, k: Self::Key, v: Self::Value);
fn remove(&mut self, k: &Self::Key) -> Option<Self::Value>;
}
#[derive(Debug, Default, Deserialize, Hash, PartialEq, PartialOrd, Eq, Ord)]
#[serde(from = "Container")]
pub enum KeyedContainerBuilder<Container, Target> {
#[default]
Unspecified,
Some(Container),
_PhantomData(PhantomData<fn() -> Target>),
}
impl<Container, Target> From<Container> for KeyedContainerBuilder<Container, Target> {
fn from(value: Container) -> Self {
Self::Some(value)
}
}
impl<Container, Target> ConfigurationBuilder for KeyedContainerBuilder<Container, Target>
where
Self: DeserializeOwned,
Container:
KeyedContainer + IntoIterator<Item = (KeyOf<Container>, ValueOf<Container>)> + 'static,
KeyOf<Container>: Display,
ValueOf<Container>: ConfigurationBuilder + 'static,
Target: Default + FromIterator<(KeyOf<Container>, TargetOf<ValueOf<Container>>)>,
for<'a> &'a Container: IntoIterator<Item = (&'a KeyOf<Container>, &'a ValueOf<Container>)>,
{
type Target = Target;
fn merge(self, other: Self) -> Self {
match (self, other) {
(Self::_PhantomData(_), _) | (_, Self::_PhantomData(_)) => {
unreachable!("PhantomData is never instantiated")
}
(Self::Unspecified, other) => other,
(us, Self::Unspecified) => us,
(Self::Some(mut us), Self::Some(other)) => {
for (key, their_val) in other {
let val = if let Some(our_val) = us.remove(&key) {
our_val.merge(their_val)
} else {
their_val
};
us.insert(key, val);
}
Self::Some(us)
}
}
}
fn try_build(self) -> Result<Self::Target, Error> {
match self {
Self::Unspecified => Err(Error::MissingValue(MissingValue::default())),
Self::Some(val) => val
.into_iter()
.map(|(key, value)| Ok((key, value.try_build()?)))
.collect(),
Self::_PhantomData(_) => unreachable!("PhantomData is never instantiated"),
}
}
fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
match self {
Self::Unspecified => Ok(false),
Self::Some(val) => val
.into_iter()
.map(|(key, value)| (key, value.contains_non_secret_data()))
.find(|(_key, result)| result.is_err())
.map(|(key, result)| result.map_err(|err| err.prepend(key.to_string())))
.unwrap_or(Ok(true)),
Self::_PhantomData(_) => unreachable!("PhantomData is never instantiated"),
}
}
}
#[derive(Debug, Default, Clone)]
pub enum MergingUnsetBuilder<T> {
#[default]
Unset,
Set(T),
}
impl<'de, T> Deserialize<'de> for MergingUnsetBuilder<T>
where
T: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
T::deserialize(deserializer).map(Self::Set)
}
}
pub trait MergingWithUnset {
type Target;
fn merge(self, other: Self) -> Self;
fn try_build(self) -> Result<Self::Target, Error>;
fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret>;
}
impl<T> ConfigurationBuilder for MergingUnsetBuilder<T>
where
T: MergingWithUnset + DeserializeOwned,
{
type Target = T::Target;
fn merge(self, other: Self) -> Self {
match (self, other) {
(Self::Unset, merged) | (merged, Self::Unset) => merged,
(Self::Set(s), Self::Set(o)) => Self::Set(s.merge(o)),
}
}
fn try_build(self) -> Result<Self::Target, Error> {
match self {
Self::Unset => Err(Error::MissingValue(MissingValue::default())),
Self::Set(s) => s.try_build(),
}
}
fn contains_non_secret_data(&self) -> Result<bool, UnexpectedSecret> {
match self {
Self::Unset => Ok(false),
Self::Set(s) => s.contains_non_secret_data(),
}
}
}