{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE NoImplicitPrelude #-}

module Data.Mergeable.IsMap
  ( IsMap (..),
    selectBy,
    selectOr,
    FromList (..),
  )
where

import Control.Monad.Except (MonadError (throwError))
#if MIN_VERSION_aeson(2,0,0)
import Data.Aeson.Key (Key)
import qualified Data.Aeson.KeyMap as A
#endif
import qualified Data.HashMap.Lazy as HM
import Data.Mergeable.Internal.Merge (mergeNoDuplicates)
import Data.Mergeable.Internal.NameCollision (NameCollision)
import Relude

class IsMap k m | m -> k where
  unsafeFromList :: [(k, a)] -> m a

  singleton :: k -> a -> m a

  lookup :: k -> m a -> Maybe a

  member :: k -> m a -> Bool
  member = Bool -> (a -> Bool) -> k -> m a -> Bool
forall k (c :: * -> *) d a.
IsMap k c =>
d -> (a -> d) -> k -> c a -> d
selectOr Bool
False (Bool -> a -> Bool
forall a b. a -> b -> a
const Bool
True)

  toAssoc :: m a -> [(k, a)]

instance (Eq k, Hashable k) => IsMap k (HashMap k) where
  unsafeFromList :: [(k, a)] -> HashMap k a
unsafeFromList = [(k, a)] -> HashMap k a
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList
  singleton :: k -> a -> HashMap k a
singleton = k -> a -> HashMap k a
forall k v. Hashable k => k -> v -> HashMap k v
HM.singleton
  lookup :: k -> HashMap k a -> Maybe a
lookup = k -> HashMap k a -> Maybe a
forall k v. (Eq k, Hashable k) => k -> HashMap k v -> Maybe v
HM.lookup
  member :: k -> HashMap k a -> Bool
member = k -> HashMap k a -> Bool
forall k a. (Eq k, Hashable k) => k -> HashMap k a -> Bool
HM.member
  toAssoc :: HashMap k a -> [(k, a)]
toAssoc = HashMap k a -> [(k, a)]
forall k v. HashMap k v -> [(k, v)]
HM.toList

#if MIN_VERSION_aeson(2,0,0)
instance IsMap Key A.KeyMap where
  unsafeFromList :: [(Key, a)] -> KeyMap a
unsafeFromList = [(Key, a)] -> KeyMap a
forall a. [(Key, a)] -> KeyMap a
A.fromList
  singleton :: Key -> a -> KeyMap a
singleton = Key -> a -> KeyMap a
forall a. Key -> a -> KeyMap a
A.singleton
  lookup :: Key -> KeyMap a -> Maybe a
lookup = Key -> KeyMap a -> Maybe a
forall a. Key -> KeyMap a -> Maybe a
A.lookup
  member :: Key -> KeyMap a -> Bool
member = Key -> KeyMap a -> Bool
forall a. Key -> KeyMap a -> Bool
A.member
  toAssoc :: KeyMap a -> [(Key, a)]
toAssoc = KeyMap a -> [(Key, a)]
forall a. KeyMap a -> [(Key, a)]
A.toList
#endif

selectBy :: (MonadError e m, IsMap k c, Monad m) => e -> k -> c a -> m a
selectBy :: e -> k -> c a -> m a
selectBy e
err = m a -> (a -> m a) -> k -> c a -> m a
forall k (c :: * -> *) d a.
IsMap k c =>
d -> (a -> d) -> k -> c a -> d
selectOr (e -> m a
forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError e
err) a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure

selectOr :: IsMap k c => d -> (a -> d) -> k -> c a -> d
selectOr :: d -> (a -> d) -> k -> c a -> d
selectOr d
fb a -> d
f k
key c a
lib = d -> (a -> d) -> Maybe a -> d
forall b a. b -> (a -> b) -> Maybe a -> b
maybe d
fb a -> d
f (k -> c a -> Maybe a
forall k (m :: * -> *) a. IsMap k m => k -> m a -> Maybe a
lookup k
key c a
lib)

class FromList m map k a where
  fromList :: (Monad m) => [(k, a)] -> m (map k a)

instance (Hashable k, Eq k, MonadError e m, NameCollision e a) => FromList m HashMap k a where
  fromList :: [(k, a)] -> m (HashMap k a)
fromList = ([(k, a)] -> HashMap k a) -> [(k, a)] -> m (HashMap k a)
forall k (m :: * -> *) e a b.
(Eq k, Hashable k, Monad m, MonadError e m, NameCollision e a) =>
([(k, a)] -> b) -> [(k, a)] -> m b
mergeNoDuplicates [(k, a)] -> HashMap k a
forall k v. (Eq k, Hashable k) => [(k, v)] -> HashMap k v
HM.fromList