{-# LANGUAGE Safe #-}
{-
Copyright (c) 2004-2011 John Goerzen <jgoerzen@complete.org>

All rights reserved.

For license and copyright information, see the file LICENSE
-}

{- |
   Module     : Data.Map.Utils
   Copyright  : Copyright (C) 2004-2011 John Goerzen
   SPDX-License-Identifier: BSD-3-Clause

   Stability  : provisional
   Portability: portable

This module provides various helpful utilities for dealing with Data.Maps.

Written by John Goerzen, jgoerzen\@complete.org
-}

module Data.Map.Utils (-- * Basic Utilities
                     flipM, flippedLookupM, forceLookupM,
                     -- * Conversions
                     strToM,
                     strFromM
                          )
where

import           Data.List.Utils (flipAL, strFromAL, strToAL)
import qualified Data.Map

{- | Converts a String, String Map into a string representation.
See 'Data.List.Utils.strFromAL' for more on the similar function for
association lists.  This implementation is simple:

>strFromM = strFromAL . Data.Map.toList

This function is designed to work with Map String String objects,
but may also work with other objects with simple representations. -}
strFromM :: (Show a, Show b, Ord a) => Data.Map.Map a b -> String
strFromM = strFromAL . Data.Map.toList

{- | Converts a String into a String, String Map.  See
'Data.List.Utils.strToAL' for more on the similar function for association
lists.

This implementation is simple:

>strToM = Data.Map.fromList . strToAL

This function is designed to work with Map String String objects,
but may work with other key\/value combinations if they have simple
representations.  -}
strToM :: (Read a, Read b, Ord a) => String -> Data.Map.Map a b
strToM = Data.Map.fromList . strToAL

{- | Flips a Map.  See 'Data.List.Utils.flipAL' for more on the similar
function for lists. -}

flipM :: (Ord key, Ord val) => Data.Map.Map key val -> Data.Map.Map val [key]
flipM = Data.Map.fromList . flipAL . Data.Map.toList

{- | Returns a list of all keys in the Map whose value matches the
parameter. If the value does not occur in the Map, the empty
list is returned. -}

flippedLookupM :: (Ord val, Ord key) => val -> Data.Map.Map key val -> [key]
flippedLookupM v fm =
    case Data.Map.lookup v (flipM fm) of
                             Nothing -> []
                             Just x  -> x

{- | Performs a lookup, and raises an exception (with an error message
prepended with the given string) if the key could not be found.
-}
forceLookupM :: (Show key, Ord key) => String -> key ->
                                       Data.Map.Map key elt -> elt
forceLookupM msg k fm =
    case Data.Map.lookup k fm of
         Just x  -> x
         Nothing -> error $ msg ++ ": could not find key " ++ (show k)