{-# LANGUAGE DataKinds #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE DeriveTraversable #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE KindSignatures #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE NoImplicitPrelude #-} module Data.Morpheus.Ext.MergeSet ( MergeSet, ) where import Data.Morpheus.Ext.Elems (Elems (..)) import Data.Morpheus.Ext.Empty (Empty (..)) import Data.Morpheus.Ext.Map ( fromListT, resolveWith, runResolutionT, ) import Data.Morpheus.Ext.SemigroupM ( SemigroupM (..), ) import Data.Morpheus.Internal.Utils ( Collection (..), Failure (..), FromElems (..), KeyOf (..), Selectable (..), toPair, ) import Data.Morpheus.Types.Internal.AST.Base ( FieldName, Ref, ValidationErrors, ) import Data.Morpheus.Types.Internal.AST.Stage ( RAW, Stage, VALID, ) import Language.Haskell.TH.Syntax (Lift (..)) import Relude -- set with mergeable components newtype MergeSet (dups :: Stage) k a = MergeSet { unpack :: [a] } deriving ( Show, Eq, Functor, Foldable, Lift, Traversable, Collection a, Elems a, Empty ) instance (KeyOf k a) => Selectable k a (MergeSet opt k a) where selectOr fb f key (MergeSet ls) = maybe fb f (find ((key ==) . keyOf) ls) instance ( KeyOf k a, SemigroupM m a, Monad m, Failure ValidationErrors m, Eq a ) => SemigroupM m (MergeSet VALID k a) where mergeM path (MergeSet x) (MergeSet y) = resolveMergable path (x <> y) resolveMergable :: ( KeyOf k a, Monad m, Eq a, SemigroupM m a, Failure ValidationErrors m ) => [Ref FieldName] -> [a] -> m (MergeSet dups k a) resolveMergable path xs = runResolutionT (fromListT (toPair <$> xs)) (MergeSet . fmap snd) (resolveWith (resolveConflict path)) instance ( KeyOf k a, SemigroupM m a, Monad m, Failure ValidationErrors m, Eq a ) => FromElems m a (MergeSet VALID k a) where fromElems = resolveMergable [] instance Applicative m => SemigroupM m (MergeSet RAW k a) where mergeM _ (MergeSet x) (MergeSet y) = pure $ MergeSet $ x <> y instance Applicative m => FromElems m a (MergeSet RAW k a) where fromElems = pure . MergeSet resolveConflict :: ( Monad m, Eq a, KeyOf k a, SemigroupM m a, Failure ValidationErrors m ) => [Ref FieldName] -> a -> a -> m a resolveConflict path oldValue newValue | oldValue == newValue = pure oldValue | otherwise = mergeM path oldValue newValue