{-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE DeriveLift #-} {-# LANGUAGE OverloadedStrings #-} module Data.Morpheus.Types.Internal.AST.MergeSet ( MergeSet , toOrderedMap , concatTraverse , join ) where import Data.Semigroup ((<>)) import Data.List (find, (\\)) import Data.Maybe (maybe) import Language.Haskell.TH.Syntax ( Lift(..) ) -- MORPHEUS import Data.Morpheus.Types.Internal.Operation ( Merge(..) , Empty(..) , Singleton(..) , Selectable(..) , Listable(..) , Failure(..) , KeyOf(..) , toPair ) import Data.Morpheus.Types.Internal.AST.Base ( Named , GQLErrors , Ref ) import Data.Morpheus.Types.Internal.AST.OrderedMap ( OrderedMap(..) ) import qualified Data.Morpheus.Types.Internal.AST.OrderedMap as OM -- set with mergeable components newtype MergeSet a = MergeSet { unpack :: [a] } deriving ( Show, Eq, Functor, Foldable , Lift ) concatTraverse :: (KeyOf a, Eq a, Eq b, Merge a, Merge b, KeyOf b, Monad m, Failure GQLErrors m) => (a -> m (MergeSet b)) -> MergeSet a -> m (MergeSet b) concatTraverse f smap = traverse f (toList smap) >>= join join :: (Eq a, KeyOf a , Merge a, Monad m, Failure GQLErrors m) => [MergeSet a] -> m (MergeSet a) join = __join empty where __join :: (Eq a,KeyOf a, Merge a, Monad m, Failure GQLErrors m) => MergeSet a ->[MergeSet a] -> m (MergeSet a) __join acc [] = pure acc __join acc (x:xs) = acc <:> x >>= (`__join` xs) toOrderedMap :: KeyOf a => MergeSet a -> OrderedMap a toOrderedMap = OM.unsafeFromValues . unpack instance Traversable MergeSet where traverse f = fmap MergeSet . traverse f . unpack instance Empty (MergeSet a) where empty = MergeSet [] instance (KeyOf a) => Singleton (MergeSet a) a where singleton x = MergeSet [x] instance KeyOf a => Selectable (MergeSet a) a where selectOr fb _ "" _ = fb selectOr fb f key (MergeSet ls) = maybe fb f (find ((key ==) . keyOf) ls) -- must merge files on collision instance (KeyOf a, Merge a, Eq a) => Merge (MergeSet a) where merge = safeJoin instance (KeyOf a, Merge a, Eq a) => Listable (MergeSet a) a where fromAssoc = safeFromList toAssoc = map toPair . unpack safeFromList :: (Monad m, KeyOf a, Eq a, Merge a ,Failure GQLErrors m) => [Named a] -> m (MergeSet a) safeFromList = insertList [] empty . map snd safeJoin :: (Monad m, KeyOf a, Eq a, Merge a ,Failure GQLErrors m) => [Ref] -> MergeSet a -> MergeSet a -> m (MergeSet a) safeJoin path hm1 hm2 = insertList path hm1 (toList hm2) insertList:: (Monad m, Eq a, KeyOf a, Merge a ,Failure GQLErrors m) => [Ref] -> MergeSet a -> [a] -> m (MergeSet a) insertList _ smap [] = pure smap insertList path smap (x:xs) = insert path smap x >>= flip (insertList path) xs insert :: (Monad m, Eq a, KeyOf a , Merge a ,Failure GQLErrors m) => [Ref] -> MergeSet a -> a -> m (MergeSet a) insert path mSet@(MergeSet ls) currentValue = MergeSet <$> __insert where __insert = selectOr (pure $ ls <> [currentValue]) mergeWith (keyOf currentValue) mSet ------------------ mergeWith oldValue | oldValue == currentValue = pure ls | otherwise = do mergedValue <- merge path oldValue currentValue pure $ ( ls \\ [oldValue]) <> [mergedValue]