{-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TemplateHaskell #-} {-# LANGUAGE DeriveFunctor #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} module Data.Morpheus.Types.Internal.AST.OrderedMap ( OrderedMap(..) , unsafeFromValues , traverseWithKey , foldWithKey , update ) where import Data.HashMap.Lazy ( HashMap ) import qualified Data.HashMap.Lazy as HM import Data.Semigroup ((<>)) import Data.Maybe (isJust,fromMaybe) import Language.Haskell.TH.Syntax ( Lift(..) ) -- MORPHEUS import Data.Morpheus.Error.NameCollision (NameCollision(..)) import Data.Morpheus.Types.Internal.Operation ( Merge(..) , Empty(..) , Singleton(..) , Selectable(..) , Listable(..) , Failure(..) , KeyOf(..) , toPair ) import Data.Morpheus.Types.Internal.AST.Base ( Name , Named , GQLErrors ) -- OrderedMap data OrderedMap a = OrderedMap { mapKeys :: [Name], mapEntries :: HashMap Name a } deriving (Show, Eq, Functor) traverseWithKey :: Applicative t => (Name -> a -> t b) -> OrderedMap a -> t (OrderedMap b) traverseWithKey f (OrderedMap names hmap) = OrderedMap names <$> HM.traverseWithKey f hmap foldWithKey :: NameCollision a => (Name -> a -> b -> b) -> b -> OrderedMap a -> b foldWithKey f defValue om = foldr (uncurry f) defValue (toAssoc om) update :: KeyOf a => a -> OrderedMap a -> OrderedMap a update x (OrderedMap names values) = OrderedMap newNames $ HM.insert name x values where name = keyOf x newNames | name `elem` names = names | otherwise = names <> [name] instance Lift a => Lift (OrderedMap a) where lift (OrderedMap names x) = [| OrderedMap names (HM.fromList ls) |] where ls = HM.toList x instance Foldable OrderedMap where foldMap f OrderedMap { mapEntries } = foldMap f mapEntries instance Traversable OrderedMap where traverse f (OrderedMap names values) = OrderedMap names <$> traverse f values instance Empty (OrderedMap a) where empty = OrderedMap [] HM.empty instance (KeyOf a) => Singleton (OrderedMap a) a where singleton x = OrderedMap [keyOf x] $ HM.singleton (keyOf x) x instance Selectable (OrderedMap a) a where selectOr fb f key OrderedMap { mapEntries } = maybe fb f (HM.lookup key mapEntries) instance NameCollision a => Merge (OrderedMap a) where merge _ (OrderedMap k1 x) (OrderedMap k2 y) = OrderedMap (k1 <> k2) <$> safeJoin x y instance NameCollision a => Listable (OrderedMap a) a where fromAssoc = safeFromList toAssoc OrderedMap { mapKeys, mapEntries } = map takeValue mapKeys where takeValue key = (key, fromMaybe (error "TODO:error") (key `HM.lookup` mapEntries )) safeFromList :: (Failure GQLErrors m, Applicative m, NameCollision a) => [Named a] -> m (OrderedMap a) safeFromList values = OrderedMap (map fst values) <$> safeUnionWith HM.empty values unsafeFromValues :: KeyOf a => [a] -> OrderedMap a unsafeFromValues x = OrderedMap (map keyOf x) $ HM.fromList $ map toPair x safeJoin :: (Failure GQLErrors m, Applicative m, NameCollision a) => HashMap Name a -> HashMap Name a -> m (HashMap Name a) safeJoin hm newls = safeUnionWith hm (HM.toList newls) safeUnionWith :: (Failure GQLErrors m, Applicative m, NameCollision a) => HashMap Name a -> [Named a] -> m (HashMap Name a) safeUnionWith hm names = case insertNoDups (hm,[]) names of (res,dupps) | null dupps -> pure res | otherwise -> failure $ map (uncurry nameCollision) dupps type NoDupHashMap a = (HashMap Name a,[Named a]) insertNoDups :: NoDupHashMap a -> [Named a] -> NoDupHashMap a insertNoDups collected [] = collected insertNoDups (coll,errors) (pair@(name,value):xs) | isJust (name `HM.lookup` coll) = insertNoDups (coll,errors <> [pair]) xs | otherwise = insertNoDups (HM.insert name value coll,errors) xs