module Web.DDP.Deadpan.DSL
( module Web.DDP.Deadpan.DSL
, module Data.EJson
, Text
, pack
)
where
import Control.Concurrent.STM
import Control.Concurrent
import Control.Applicative
import Network.WebSockets
import Control.Monad.Reader
import Control.Lens
import Data.Monoid
import Data.Text
import Web.DDP.Deadpan.Comms
import Data.EJson
data LookupItem a = LI { _ident :: Maybe Text, _messageType :: Maybe Text, _body :: a }
makeLenses ''LookupItem
type Lookup a = [ LookupItem a ]
data AppState cb = AppState
{ _callbackSet :: Lookup cb
, _collections :: EJsonValue
, _connection :: Network.WebSockets.Connection
}
makeLenses ''AppState
type Callback = EJsonValue -> DeadpanApp ()
newtype DeadpanApp a = DeadpanApp
{ _deadpanApp :: ReaderT
(TVar (AppState Callback))
IO
a
}
instance Monad DeadpanApp where
return = DeadpanApp . return
s >>= f = DeadpanApp $ _deadpanApp s >>= _deadpanApp . f
instance Functor DeadpanApp where
fmap f (DeadpanApp m) = DeadpanApp $ fmap f m
instance Applicative DeadpanApp where
pure = DeadpanApp . pure
(DeadpanApp f) <*> (DeadpanApp m) = DeadpanApp (f <*> m)
instance MonadIO DeadpanApp where
liftIO i = DeadpanApp $ liftIO i
makeLenses ''DeadpanApp
runDeadpan :: DeadpanApp a
-> TVar (AppState Callback)
-> IO a
runDeadpan app appState = runReaderT (_deadpanApp app) appState
setHandler :: LookupItem Callback -> DeadpanApp ()
setHandler i = modifyAppState foo
where foo x = x &~ callbackSet %= (i:)
setIdHandler :: Text -> Callback -> DeadpanApp ()
setIdHandler myid cb = setHandler $ LI (Just myid) Nothing cb
setMsgHandler :: Text -> Callback -> DeadpanApp ()
setMsgHandler msg cb = setHandler $ LI Nothing (Just msg) cb
setCatchAllHandler :: Callback -> DeadpanApp ()
setCatchAllHandler cb = setHandler $ LI Nothing Nothing cb
deleteHandlerID :: Text -> DeadpanApp ()
deleteHandlerID k = modifyAppState foo
where foo x = x &~ callbackSet %= Prelude.filter bar
bar y = _ident y == Nothing || _ident y /= Just k
modifyAppState :: (AppState Callback -> AppState Callback) -> DeadpanApp ()
modifyAppState f = DeadpanApp
$ do st <- ask
liftIO $ atomically $ do s <- readTVar st
writeTVar st (f s)
getAppState :: DeadpanApp (AppState Callback)
getAppState = DeadpanApp $ ask >>= liftIO . atomically . readTVar
sendData :: EJsonValue -> DeadpanApp ()
sendData v = getAppState >>= liftIO . flip sendEJ v . _connection
sendMessage :: Text -> EJsonValue -> DeadpanApp ()
sendMessage key m = sendData messageData
where
messageData = ejobject [("msg", ejstring key)] `mappend` m
connect :: DeadpanApp ()
connect = sendMessage "connect" $
ejobject [ ("version", "1")
, ("support", ejarray ["1","pre2","pre1"]) ]
fork :: DeadpanApp a -> DeadpanApp ThreadId
fork app = do
st <- DeadpanApp ask
liftIO $ forkIO $ void $ runDeadpan app st
fetchMessages :: DeadpanApp ()
fetchMessages = void $
fork $
forever $ do message <- getServerMessage
as <- getAppState
fork $ respondToMessage (_callbackSet as) message
getServerMessage :: DeadpanApp (Maybe EJsonValue)
getServerMessage = getAppState >>= liftIO . getEJ . _connection
(=?) :: Eq a => Maybe a -> Maybe a -> Bool
x@(Just _) =? y = x == y
_ =? _ = True
respondToMessage :: Lookup Callback -> Maybe EJsonValue -> DeadpanApp ()
respondToMessage _ Nothing = return ()
respondToMessage cbSet (Just message) = do
let maybeMsgName = message ^? _EJObjectKeyString "msg"
maybeID = message ^? _EJObjectKeyString "id"
forM_ cbSet $ \x -> do
when (_ident x =? maybeID && _messageType x =? maybeMsgName)
(_body x message)