hbro-1.3.0.0: library/Hbro/IPC.hs
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeFamilies #-}
-- | Designed to be imported as @qualified@.
module Hbro.IPC
( CommandMap
, bindCommands
-- , sendMessage
) where
-- {{{ Imports
import Hbro.Logger
import Hbro.Prelude
import Data.Function (fix)
import qualified Data.Map as Map
import System.ZMQ4 (Receiver, Rep (..), Sender, Socket)
import qualified System.ZMQ4 as ZMQ
-- }}}
-- | Commands are mere 'Text's
type Command = Text
-- | Arguments are 'Text's too
type Argument = Text
-- | Responses may be OK or KO
type Response = Either Text Text
type CommandMap m = Map Command ([Argument] -> m Response)
withContext :: ControlIO m => (ZMQ.Context -> m a) -> m (StM m a)
withContext f = liftBaseWith $ \runInBase -> ZMQ.withContext (runInBase . f)
withSocket :: (ZMQ.SocketType s, ControlIO m) => ZMQ.Context -> s -> (Socket s -> m a) -> m (StM m a)
withSocket c s f = liftBaseWith $ \runInBase -> ZMQ.withSocket c s (runInBase . f)
bindCommands :: (ControlIO m, MonadLogger m) => Text -> CommandMap m -> m ()
bindCommands uri commandMap = void . withContext $ \c -> withSocket c Rep $ \socket -> do
info $ "Opening IPC socket at: " ++ uri
io $ ZMQ.bind socket (unpack uri)
fix $ \recurse -> do
message <- receive socket
debug $ "Received command: " ++ message
case words message of
[] -> send socket "ERROR Empty command"
command:arguments -> do
response <- case Map.lookup command commandMap of
Just f -> either ("ERROR " ++) id <$> f arguments
_ -> return "ERROR Unknown command"
debug $ "Sending response: " ++ tshow response
send socket response
recurse
-- {{{ Utils
-- | Send message to given socket
send :: (BaseIO m, Sender a) => Socket a -> Text -> m ()
send socket = io . ZMQ.send socket [] . encodeUtf8
-- | Send a single command to the given socket (which must be 'Rep'), and return the answer
-- sendMessage :: (BaseIO m)
-- => Context
-- -> Text -- ^ Target socket URI
-- -> Text -- ^ Message
-- -> m Text
-- sendMessage context socketURI message = do
-- socket <- io $ ZMQ.socket context Req
-- io . ZMQ.bind socket $ unpack socketURI
-- debug $ "Sending message to IPC socket at: " ++ socketURI
-- io . ZMQ.send socket [] $ encodeUtf8 message
-- decodeUtf8 <$> io (ZMQ.receive socket)
-- | Wait for a message to be received from given socket
receive :: (BaseIO m, Receiver a) => Socket a -> m Text
receive socket = decodeUtf8 <$> io (ZMQ.receive socket)
-- | Same as 'sendMessage', but for all running instances of the browser.
-- sendMessageToAll :: (MonadBase IO m, MonadReader t m, HasConfig t) => String -> m [String]
-- sendMessageToAll message = do
-- dir <- readConfig socketDir
-- getAllProcessIDs >>= mapM ((`sendMessage` message) . (`socketPath` dir))
-- }}}