content: {-# 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
  {- ORMOLU_DISABLE -}
  import qualified Data.HashMap.Lazy as HM
  import qualified Data.Map as M
  {- ORMOLU_ENABLE -}
  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 = selectOr False (const True)
  
    toAssoc :: m a -> [(k, a)]
  
  instance (Eq k, Hashable k) => IsMap k (HashMap k) where
    unsafeFromList = HM.fromList
    singleton = HM.singleton
    lookup = HM.lookup
    member = HM.member
    toAssoc = HM.toList
  
  instance (Eq k, Ord k) => IsMap k (Map k) where
    unsafeFromList = M.fromList
    singleton = M.singleton
    lookup = M.lookup
    member = M.member
    toAssoc = M.toList
  
  #if MIN_VERSION_aeson(2,0,0)
  instance IsMap Key A.KeyMap where
    unsafeFromList = A.fromList
    singleton = A.singleton
    lookup = A.lookup
    member = A.member
    toAssoc = A.toList
  #endif
  
  selectBy :: (MonadError e m, IsMap k c, Monad m) => e -> k -> c a -> m a
  selectBy err = selectOr (throwError err) pure
  
  selectOr :: (IsMap k c) => d -> (a -> d) -> k -> c a -> d
  selectOr fb f key lib = maybe fb f (lookup key 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 = mergeNoDuplicates HM.fromList
  