mirror of
https://github.com/IHaskell/IHaskell.git
synced 2025-04-16 11:26:08 +00:00
adding message directory
This commit is contained in:
parent
959dc3a14e
commit
78940c2d65
46
IHaskell/Message/BodyParser.hs
Normal file
46
IHaskell/Message/BodyParser.hs
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
-- | This module provides parsers for message bodies for
|
||||||
|
-- @IHaskell.Message.Parser@, and should not be used elsewhere. The
|
||||||
|
-- 'header' field on all these messages is initialized to 'undefined' and
|
||||||
|
-- is inserted afterwards.
|
||||||
|
module IHaskell.Message.BodyParser (
|
||||||
|
kernelInfoRequestParser,
|
||||||
|
executeRequestParser
|
||||||
|
) where
|
||||||
|
|
||||||
|
import BasicPrelude
|
||||||
|
import Data.Aeson
|
||||||
|
import Data.Aeson.Types (parse)
|
||||||
|
|
||||||
|
import IHaskell.Types
|
||||||
|
|
||||||
|
-- | Parse a kernel info request.
|
||||||
|
-- A kernel info request has no auxiliary information, so ignore the body.
|
||||||
|
kernelInfoRequestParser :: LByteString -> Message
|
||||||
|
kernelInfoRequestParser _ = KernelInfoRequest { header = undefined }
|
||||||
|
|
||||||
|
-- | Parse an execute request.
|
||||||
|
-- Fields used are:
|
||||||
|
-- 1. "code": the code to execute.
|
||||||
|
-- 2. "silent": whether to execute silently.
|
||||||
|
-- 3. "store_history": whether to include this in history.
|
||||||
|
-- 4. "allow_stdin": whether to allow reading from stdin for this code.
|
||||||
|
executeRequestParser :: LByteString -> Message
|
||||||
|
executeRequestParser content =
|
||||||
|
let parser obj = do
|
||||||
|
code <- obj .: "code"
|
||||||
|
silent <- obj .: "silent"
|
||||||
|
storeHistory <- obj .: "store_history"
|
||||||
|
allowStdin <- obj .: "allow_stdin"
|
||||||
|
|
||||||
|
return (code, silent, storeHistory, allowStdin)
|
||||||
|
Just decoded = decode content
|
||||||
|
Success (code, silent, storeHistory, allowStdin) = parse parser decoded in
|
||||||
|
ExecuteRequest {
|
||||||
|
header = undefined,
|
||||||
|
getCode = code,
|
||||||
|
getSilent = silent,
|
||||||
|
getAllowStdin = allowStdin,
|
||||||
|
getStoreHistory = storeHistory,
|
||||||
|
getUserVariables = [],
|
||||||
|
getUserExpressions = []
|
||||||
|
}
|
72
IHaskell/Message/Parser.hs
Normal file
72
IHaskell/Message/Parser.hs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
-- | This module is responsible for converting from low-level ByteStrings
|
||||||
|
-- | obtained from the 0MQ sockets into Messages. The only exposed function is
|
||||||
|
-- | `parseMessage`, which should only be used in the low-level 0MQ interface.
|
||||||
|
module IHaskell.Message.Parser (parseMessage) where
|
||||||
|
|
||||||
|
import BasicPrelude
|
||||||
|
import Data.Aeson ((.:), decode, Result(..), Object)
|
||||||
|
import Data.Aeson.Types (parse)
|
||||||
|
|
||||||
|
import qualified Data.ByteString.Lazy as Lazy
|
||||||
|
|
||||||
|
import IHaskell.Types
|
||||||
|
import IHaskell.Message.BodyParser
|
||||||
|
|
||||||
|
----- External interface -----
|
||||||
|
|
||||||
|
-- | Parse a message from its ByteString components into a Message.
|
||||||
|
parseMessage :: [ByteString] -- ^ The list of identifiers sent with the message.
|
||||||
|
-> ByteString -- ^ The header data.
|
||||||
|
-> ByteString -- ^ The parent header, which is just "{}" if there is no header.
|
||||||
|
-> ByteString -- ^ The metadata map, also "{}" for an empty map.
|
||||||
|
-> ByteString -- ^ The message content.
|
||||||
|
-> Message -- ^ A parsed message.
|
||||||
|
parseMessage idents headerData parentHeader metadata content =
|
||||||
|
let header = parseHeader idents headerData parentHeader metadata
|
||||||
|
messageType = msgType header in
|
||||||
|
(parser messageType $ Lazy.fromStrict content) {
|
||||||
|
header = header
|
||||||
|
}
|
||||||
|
|
||||||
|
----- Module internals -----
|
||||||
|
|
||||||
|
-- | Parse a header from its ByteString components into a MessageHeader.
|
||||||
|
parseHeader :: [ByteString] -- ^ The list of identifiers.
|
||||||
|
-> ByteString -- ^ The header data.
|
||||||
|
-> ByteString -- ^ The parent header, or "{}" for Nothing.
|
||||||
|
-> ByteString -- ^ The metadata, or "{}" for an empty map.
|
||||||
|
-> MessageHeader -- The resulting message header.
|
||||||
|
parseHeader idents headerData parentHeader metadata = MessageHeader {
|
||||||
|
identifiers = idents,
|
||||||
|
parentHeader = parentResult,
|
||||||
|
metadata = metadataMap,
|
||||||
|
messageId = messageUUID,
|
||||||
|
sessionId = sessionUUID,
|
||||||
|
username = username,
|
||||||
|
msgType = messageType
|
||||||
|
} where
|
||||||
|
-- Decode the header data and the parent header data into JSON objects.
|
||||||
|
-- If the parent header data is absent, just have Nothing instead.
|
||||||
|
Just result = decode $ Lazy.fromStrict headerData :: Maybe Object
|
||||||
|
parentResult = if parentHeader == "{}"
|
||||||
|
then Nothing
|
||||||
|
else Just $ parseHeader idents parentHeader "{}" metadata
|
||||||
|
|
||||||
|
-- Get the basic fields from the header.
|
||||||
|
Success (messageType, username, messageUUID, sessionUUID) = flip parse result $ \obj -> do
|
||||||
|
messType <- obj .: "msg_type"
|
||||||
|
username <- obj .: "username"
|
||||||
|
message <- obj .: "msg_id"
|
||||||
|
session <- obj .: "session"
|
||||||
|
return (messType, username, message, session)
|
||||||
|
|
||||||
|
-- Get metadata as a simple map.
|
||||||
|
Just metadataMap = decode $ Lazy.fromStrict metadata :: Maybe (Map ByteString ByteString)
|
||||||
|
|
||||||
|
parser :: MessageType -- ^ The message type being parsed.
|
||||||
|
-> LByteString -> Message -- The parser that converts the body into a message.
|
||||||
|
-- This message should have an undefined
|
||||||
|
-- header.
|
||||||
|
parser "kernel_info_request" = kernelInfoRequestParser
|
||||||
|
parser "execute_request" = executeRequestParser
|
||||||
|
parser other = error $ "Unknown message type " ++ textToString (show other)
|
50
IHaskell/Message/Writer.hs
Normal file
50
IHaskell/Message/Writer.hs
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
-- | This module contains the @ToJSON@ instance for @Message@.
|
||||||
|
module IHaskell.Message.Writer (
|
||||||
|
ToJSON(..)
|
||||||
|
) where
|
||||||
|
|
||||||
|
import BasicPrelude
|
||||||
|
import Data.Aeson
|
||||||
|
import Data.Map (fromList)
|
||||||
|
|
||||||
|
import IHaskell.Types
|
||||||
|
|
||||||
|
-- Convert message bodies into JSON.
|
||||||
|
instance ToJSON Message where
|
||||||
|
toJSON KernelInfoReply{} = object [
|
||||||
|
"protocol_version" .= ints [4, 0], -- current protocol version, major and minor
|
||||||
|
"language_version" .= ints [7, 6, 2],
|
||||||
|
"language" .= string "haskell"
|
||||||
|
]
|
||||||
|
|
||||||
|
toJSON ExecuteReply{ status = status, executionCounter = counter} = object [
|
||||||
|
"status" .= status,
|
||||||
|
"execution_count" .= counter,
|
||||||
|
"payload" .= emptyList,
|
||||||
|
"user_variables" .= emptyMap,
|
||||||
|
"user_expressions" .= emptyMap
|
||||||
|
]
|
||||||
|
toJSON IopubStatus{ executionState = executionState } = object [
|
||||||
|
"execution_state" .= executionState
|
||||||
|
]
|
||||||
|
|
||||||
|
toJSON body = error $ "Do not know how to convert to JSON for message " ++ textToString (show body)
|
||||||
|
|
||||||
|
instance ToJSON ExecutionState where
|
||||||
|
toJSON Busy = String "busy"
|
||||||
|
toJSON Idle = String "idle"
|
||||||
|
toJSON Starting = String "starting"
|
||||||
|
|
||||||
|
----- Constants -----
|
||||||
|
|
||||||
|
emptyMap :: Map String String
|
||||||
|
emptyMap = fromList []
|
||||||
|
|
||||||
|
emptyList :: [Int]
|
||||||
|
emptyList = []
|
||||||
|
|
||||||
|
ints :: [Int] -> [Int]
|
||||||
|
ints = id
|
||||||
|
|
||||||
|
string :: String -> String
|
||||||
|
string = id
|
Loading…
x
Reference in New Issue
Block a user