-----------------------------------------------------------------------------
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-----------------------------------------------------------------------------
-- |
-- Module      :  Miso.Data.Map
-- Copyright   :  (C) 2016-2025 David M. Johnson (@dmjio)
-- License     :  BSD3-style (see the file LICENSE)
-- Maintainer  :  David M. Johnson <code@dmj.io>
-- Stability   :  experimental
-- Portability :  non-portable
--
-- Mutable 'Map' data structure in 'IO'.
--
-- A JavaScript [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map). This is a convenience for manipulating JavaScript data structures from Haskell.
--
-- We recommend using this module qualified.
--
-- > import qualified Miso.Data.Map as M
--
-----------------------------------------------------------------------------
module Miso.Data.Map
  ( -- * Type
    Map
    -- * Construction
  , new
  , fromList
    -- * Operations
  , insert
  , lookup
  , clear
  , size
  , has
  , delete
  ) where
-----------------------------------------------------------------------------
import           Control.Monad (void, forM_)
import           Prelude hiding (lookup)
-----------------------------------------------------------------------------
import           Miso.DSL (jsg, JSVal, ToJSVal, FromJSVal, (!))
import qualified Miso.DSL as DSL
import           Miso.FFI (callFunction)
-----------------------------------------------------------------------------
newtype Map key value = Map JSVal deriving (JSVal -> IO (Maybe (Map key value))
JSVal -> IO (Map key value)
(JSVal -> IO (Maybe (Map key value)))
-> (JSVal -> IO (Map key value)) -> FromJSVal (Map key value)
forall a. (JSVal -> IO (Maybe a)) -> (JSVal -> IO a) -> FromJSVal a
forall key value. JSVal -> IO (Maybe (Map key value))
forall key value. JSVal -> IO (Map key value)
$cfromJSVal :: forall key value. JSVal -> IO (Maybe (Map key value))
fromJSVal :: JSVal -> IO (Maybe (Map key value))
$cfromJSValUnchecked :: forall key value. JSVal -> IO (Map key value)
fromJSValUnchecked :: JSVal -> IO (Map key value)
FromJSVal, Map key value -> IO JSVal
(Map key value -> IO JSVal) -> ToJSVal (Map key value)
forall a. (a -> IO JSVal) -> ToJSVal a
forall key value. Map key value -> IO JSVal
$ctoJSVal :: forall key value. Map key value -> IO JSVal
toJSVal :: Map key value -> IO JSVal
ToJSVal)
-----------------------------------------------------------------------------
-- | Constructs a new JS [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) in t'IO'.
--
new :: IO (Map key value)
new :: forall key value. IO (Map key value)
new = JSVal -> Map key value
forall key value. JSVal -> Map key value
Map (JSVal -> Map key value) -> IO JSVal -> IO (Map key value)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> IO JSVal -> [JSVal] -> IO JSVal
forall constructor args.
(ToObject constructor, ToArgs args) =>
constructor -> args -> IO JSVal
DSL.new (MisoString -> IO JSVal
jsg MisoString
"Map") ([] :: [JSVal])
-----------------------------------------------------------------------------
-- | Inserts a value into the t'Map' by key.
insert :: (ToJSVal key, ToJSVal value) => key -> value -> Map key value -> IO ()
insert :: forall key value.
(ToJSVal key, ToJSVal value) =>
key -> value -> Map key value -> IO ()
insert key
key value
value (Map JSVal
m) = do
  _ <- JSVal -> MisoString -> (key, value) -> IO JSVal
forall args. ToArgs args => JSVal -> MisoString -> args -> IO JSVal
callFunction JSVal
m MisoString
"set" (key
key, value
value)
  pure ()
-----------------------------------------------------------------------------
-- | Finds a value in the t'Map' by key.
lookup :: (ToJSVal key, FromJSVal value) => key -> Map key value -> IO (Maybe value)
lookup :: forall key value.
(ToJSVal key, FromJSVal value) =>
key -> Map key value -> IO (Maybe value)
lookup key
key (Map JSVal
m) = JSVal -> IO (Maybe value)
forall a. FromJSVal a => JSVal -> IO a
DSL.fromJSValUnchecked (JSVal -> IO (Maybe value)) -> IO JSVal -> IO (Maybe value)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal -> MisoString -> JSVal -> IO JSVal
forall args. ToArgs args => JSVal -> MisoString -> args -> IO JSVal
callFunction JSVal
m MisoString
"get" (JSVal -> IO JSVal) -> IO JSVal -> IO JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< key -> IO JSVal
forall a. ToJSVal a => a -> IO JSVal
DSL.toJSVal key
key
-----------------------------------------------------------------------------
-- | Empties the t'Map'.
clear :: Map key value -> IO ()
clear :: forall key value. Map key value -> IO ()
clear (Map JSVal
m) = IO JSVal -> IO ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (JSVal -> MisoString -> () -> IO JSVal
forall args. ToArgs args => JSVal -> MisoString -> args -> IO JSVal
callFunction JSVal
m MisoString
"clear" ())
-----------------------------------------------------------------------------
-- | Return the size of t'Map'.
size :: Map key value -> IO Int
size :: forall key value. Map key value -> IO Int
size (Map JSVal
m) = JSVal -> IO Int
forall a. FromJSVal a => JSVal -> IO a
DSL.fromJSValUnchecked (JSVal -> IO Int) -> IO JSVal -> IO Int
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal
m JSVal -> MisoString -> IO JSVal
forall o. ToObject o => o -> MisoString -> IO JSVal
! MisoString
"size"
-----------------------------------------------------------------------------
-- | Checks existence of a value by 'key', returns t'Bool.
has :: ToJSVal key => key -> Map key value -> IO Bool
has :: forall key value. ToJSVal key => key -> Map key value -> IO Bool
has key
key (Map JSVal
m) = JSVal -> IO Bool
forall a. FromJSVal a => JSVal -> IO a
DSL.fromJSValUnchecked (JSVal -> IO Bool) -> IO JSVal -> IO Bool
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal -> MisoString -> JSVal -> IO JSVal
forall args. ToArgs args => JSVal -> MisoString -> args -> IO JSVal
callFunction JSVal
m MisoString
"has" (JSVal -> IO JSVal) -> IO JSVal -> IO JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< key -> IO JSVal
forall a. ToJSVal a => a -> IO JSVal
DSL.toJSVal key
key
-----------------------------------------------------------------------------
-- | Removes an entry from a list, returns if the value was removed as t'Bool'.
delete :: ToJSVal key => key -> Map key value -> IO Bool
delete :: forall key value. ToJSVal key => key -> Map key value -> IO Bool
delete key
key (Map JSVal
m) = JSVal -> IO Bool
forall a. FromJSVal a => JSVal -> IO a
DSL.fromJSValUnchecked (JSVal -> IO Bool) -> IO JSVal -> IO Bool
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< JSVal -> MisoString -> JSVal -> IO JSVal
forall args. ToArgs args => JSVal -> MisoString -> args -> IO JSVal
callFunction JSVal
m MisoString
"delete" (JSVal -> IO JSVal) -> IO JSVal -> IO JSVal
forall (m :: * -> *) a b. Monad m => (a -> m b) -> m a -> m b
=<< key -> IO JSVal
forall a. ToJSVal a => a -> IO JSVal
DSL.toJSVal key
key
-----------------------------------------------------------------------------
-- | Construct a t'Map' from a list of key value pairs.
fromList :: (ToJSVal key, ToJSVal value) => [(key, value)] -> IO (Map key value)
fromList :: forall key value.
(ToJSVal key, ToJSVal value) =>
[(key, value)] -> IO (Map key value)
fromList [(key, value)]
xs = do
  m <- IO (Map key value)
forall key value. IO (Map key value)
new
  forM_ xs $ \(key
k,value
v) ->
    key -> value -> Map key value -> IO ()
forall key value.
(ToJSVal key, ToJSVal value) =>
key -> value -> Map key value -> IO ()
insert key
k value
v Map key value
m
  pure m
-----------------------------------------------------------------------------