#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum ScalarAction {
Replace,
Keep,
Merge,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum SequenceMerge {
Append,
Prepend,
Extend,
Union,
Intersect,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum OptionMerge {
Take,
Preserve,
OrLeft,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum NumberMerge {
Replace,
Keep,
Max,
Min,
Sum,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum MapMerge {
Overlay,
Union,
Left,
Right,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum BoolMerge {
Replace,
Keep,
TrueWins,
FalseWins,
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum StringMerge {
Replace,
Keep,
Concat,
ConcatWithSep(&'static str),
}
#[derive(Debug)]
pub enum Condition {
Always,
NonEmpty,
NonDefault,
Some,
Changed,
ChangedBy(fn()),
}
impl Clone for Condition {
fn clone(&self) -> Self {
match self {
Condition::Always => Condition::Always,
Condition::NonEmpty => Condition::NonEmpty,
Condition::NonDefault => Condition::NonDefault,
Condition::Some => Condition::Some,
Condition::Changed => Condition::Changed,
Condition::ChangedBy(f) => Condition::ChangedBy(*f),
}
}
}
impl PartialEq for Condition {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Condition::Always, Condition::Always)
| (Condition::NonEmpty, Condition::NonEmpty)
| (Condition::NonDefault, Condition::NonDefault)
| (Condition::Some, Condition::Some)
| (Condition::Changed, Condition::Changed) => true,
(Condition::ChangedBy(f1), Condition::ChangedBy(f2)) => core::ptr::eq(std::ptr::from_ref::<fn()>(f1), std::ptr::from_ref::<fn()>(f2)),
_ => false,
}
}
}
impl Eq for Condition {}
pub trait Policy: Clone {
fn scalar_action(&self) -> ScalarAction {
ScalarAction::Replace
}
fn struct_action(&self) -> ScalarAction {
ScalarAction::Merge
}
fn option_merge(&self) -> OptionMerge {
OptionMerge::Take
}
fn sequence_merge(&self) -> SequenceMerge {
SequenceMerge::Append
}
fn sequence_dedupe(&self) -> bool {
false
}
fn map_merge(&self) -> MapMerge {
MapMerge::Overlay
}
fn number_merge(&self) -> NumberMerge {
NumberMerge::Replace
}
fn bool_merge(&self) -> BoolMerge {
BoolMerge::Replace
}
fn string_merge(&self) -> StringMerge {
StringMerge::Replace
}
fn when_condition(&self) -> Condition {
Condition::Always
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DefaultPolicy;
impl Policy for DefaultPolicy {
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StrictReplacePolicy;
impl Policy for StrictReplacePolicy {
fn struct_action(&self) -> ScalarAction {
ScalarAction::Replace
}
fn map_merge(&self) -> MapMerge {
MapMerge::Right
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct PreservePolicy;
impl Policy for PreservePolicy {
fn scalar_action(&self) -> ScalarAction {
ScalarAction::Keep
}
fn struct_action(&self) -> ScalarAction {
ScalarAction::Keep
}
fn option_merge(&self) -> OptionMerge {
OptionMerge::Preserve
}
fn sequence_merge(&self) -> SequenceMerge {
SequenceMerge::Extend }
fn map_merge(&self) -> MapMerge {
MapMerge::Left
}
fn number_merge(&self) -> NumberMerge {
NumberMerge::Keep }
fn bool_merge(&self) -> BoolMerge {
BoolMerge::Keep
}
fn string_merge(&self) -> StringMerge {
StringMerge::Keep
}
fn when_condition(&self) -> Condition {
Condition::Always }
}
#[derive(Debug, Clone, Copy, Default)]
pub struct DedupeCollectionsPolicy;
impl Policy for DedupeCollectionsPolicy {
fn sequence_merge(&self) -> SequenceMerge {
SequenceMerge::Union
}
fn sequence_dedupe(&self) -> bool {
true
}
}
#[derive(Debug, Clone)]
pub struct ComposedPolicy<P: Policy> {
pub base: P,
pub scalar_action: Option<ScalarAction>,
pub struct_action: Option<ScalarAction>,
pub option_merge: Option<OptionMerge>,
pub sequence_merge: Option<SequenceMerge>,
pub sequence_dedupe: Option<bool>,
pub map_merge: Option<MapMerge>,
pub number_merge: Option<NumberMerge>,
pub bool_merge: Option<BoolMerge>,
pub string_merge: Option<StringMerge>,
pub when_condition: Option<Condition>,
}
impl<P: Policy> ComposedPolicy<P> {
pub fn new(base: P) -> Self {
Self {
base,
scalar_action: None,
struct_action: None,
option_merge: None,
sequence_merge: None,
sequence_dedupe: None,
map_merge: None,
number_merge: None,
bool_merge: None,
string_merge: None,
when_condition: None,
}
}
#[must_use]
pub fn with_scalar_action(mut self, action: ScalarAction) -> Self {
self.scalar_action = Some(action);
self
}
#[must_use]
pub fn with_struct_action(mut self, action: ScalarAction) -> Self {
self.struct_action = Some(action);
self
}
#[must_use]
pub fn with_option_merge(mut self, merge: OptionMerge) -> Self {
self.option_merge = Some(merge);
self
}
#[must_use]
pub fn with_sequence_merge(mut self, merge: SequenceMerge) -> Self {
self.sequence_merge = Some(merge);
self
}
#[must_use]
pub fn with_sequence_dedupe(mut self, dedupe: bool) -> Self {
self.sequence_dedupe = Some(dedupe);
self
}
#[must_use]
pub fn with_map_merge(mut self, merge: MapMerge) -> Self {
self.map_merge = Some(merge);
self
}
#[must_use]
pub fn with_number_merge(mut self, merge: NumberMerge) -> Self {
self.number_merge = Some(merge);
self
}
#[must_use]
pub fn with_bool_merge(mut self, merge: BoolMerge) -> Self {
self.bool_merge = Some(merge);
self
}
#[must_use]
pub fn with_string_merge(mut self, merge: StringMerge) -> Self {
self.string_merge = Some(merge);
self
}
#[must_use]
pub fn with_when_condition(mut self, condition: Condition) -> Self {
self.when_condition = Some(condition);
self
}
}
impl<P: Policy> Policy for ComposedPolicy<P> {
fn scalar_action(&self) -> ScalarAction {
self.scalar_action.unwrap_or_else(|| self.base.scalar_action())
}
fn struct_action(&self) -> ScalarAction {
self.struct_action.unwrap_or_else(|| self.base.struct_action())
}
fn option_merge(&self) -> OptionMerge {
self.option_merge.unwrap_or_else(|| self.base.option_merge())
}
fn sequence_merge(&self) -> SequenceMerge {
self.sequence_merge.unwrap_or_else(|| self.base.sequence_merge())
}
fn sequence_dedupe(&self) -> bool {
self.sequence_dedupe.unwrap_or_else(|| self.base.sequence_dedupe())
}
fn map_merge(&self) -> MapMerge {
self.map_merge.unwrap_or_else(|| self.base.map_merge())
}
fn number_merge(&self) -> NumberMerge {
self.number_merge.unwrap_or_else(|| self.base.number_merge())
}
fn bool_merge(&self) -> BoolMerge {
self.bool_merge.unwrap_or_else(|| self.base.bool_merge())
}
fn string_merge(&self) -> StringMerge {
self.string_merge.clone().unwrap_or_else(|| self.base.string_merge())
}
fn when_condition(&self) -> Condition {
self.when_condition.clone().unwrap_or_else(|| self.base.when_condition())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MergeOutcome {
Unchanged,
Changed,
}
impl MergeOutcome {
#[must_use]
pub fn is_changed(&self) -> bool {
matches!(self, MergeOutcome::Changed)
}
#[must_use]
pub fn is_unchanged(&self) -> bool {
matches!(self, MergeOutcome::Unchanged)
}
}
pub trait DeepMerge<P: Policy = DefaultPolicy>: Sized {
fn merge_with_policy(&mut self, other: Self, policy: &P);
fn merge_ref(&mut self, src: &Self, policy: &P)
where
Self: Clone,
{
self.merge_with_policy(src.clone(), policy);
}
fn merge_with_policy_reporting(&mut self, other: Self, policy: &P) -> MergeOutcome {
self.merge_with_policy(other, policy);
MergeOutcome::Changed
}
fn merge_ref_reporting(&mut self, src: &Self, policy: &P) -> MergeOutcome
where
Self: Clone,
{
self.merge_with_policy_reporting(src.clone(), policy)
}
#[must_use]
fn merged_with_policy(mut self, other: Self, policy: &P) -> Self {
self.merge_with_policy(other, policy);
self
}
}
pub trait DeepMergeFrom<U, P: Policy = DefaultPolicy> {
fn merge_from_with_policy(&mut self, other: U, policy: &P);
fn merge_from_with_policy_reporting(&mut self, other: U, policy: &P) -> MergeOutcome {
self.merge_from_with_policy(other, policy);
MergeOutcome::Changed
}
}
pub trait DeepMergeDefault: DeepMerge<DefaultPolicy> {
fn merge(&mut self, other: Self) {
self.merge_with_policy(other, &DefaultPolicy);
}
fn merge_ref(&mut self, src: &Self)
where
Self: Clone,
{
DeepMerge::merge_ref(self, src, &DefaultPolicy);
}
fn merge_reporting(&mut self, other: Self) -> MergeOutcome {
self.merge_with_policy_reporting(other, &DefaultPolicy)
}
fn merge_ref_reporting(&mut self, src: &Self) -> MergeOutcome
where
Self: Clone,
{
DeepMerge::merge_ref_reporting(self, src, &DefaultPolicy)
}
#[must_use]
fn merged(self, other: Self) -> Self {
self.merged_with_policy(other, &DefaultPolicy)
}
}
impl<T: DeepMerge<DefaultPolicy>> DeepMergeDefault for T {}
pub trait DeepMergeFromDefault<U>: DeepMergeFrom<U, DefaultPolicy> {
fn merge_from(&mut self, other: U) {
self.merge_from_with_policy(other, &DefaultPolicy);
}
fn merge_from_reporting(&mut self, other: U) -> MergeOutcome {
self.merge_from_with_policy_reporting(other, &DefaultPolicy)
}
}
impl<T: DeepMergeFrom<U, DefaultPolicy>, U> DeepMergeFromDefault<U> for T {}
impl<T: DeepMerge<P>, P: Policy> DeepMergeFrom<T, P> for T {
fn merge_from_with_policy(&mut self, other: T, policy: &P) {
self.merge_with_policy(other, policy);
}
fn merge_from_with_policy_reporting(&mut self, other: T, policy: &P) -> MergeOutcome {
self.merge_with_policy_reporting(other, policy)
}
}
impl<T: DeepMerge<P> + Clone, P: Policy> DeepMergeFrom<&T, P> for T {
fn merge_from_with_policy(&mut self, other: &T, policy: &P) {
self.merge_ref(other, policy);
}
fn merge_from_with_policy_reporting(&mut self, other: &T, policy: &P) -> MergeOutcome {
self.merge_ref_reporting(other, policy)
}
}