module Control.Remote.Monad.JSON.Types (
RPC(..)
, Notification(..)
, Method(..)
, Args(..)
, JSONCall(..)
, mkMethodCall
, SendAPI(..)
, ReceiveAPI(..)
, Session(..)
, ErrorMessage(..)
, Response(..)
, IDTag
, Replies
, parseReply
, parseMethodResult
) where
import Control.Applicative
import Control.Natural
import Control.Remote.Monad(RemoteMonad)
import Data.Aeson
import Data.Aeson.Types
import qualified Data.HashMap.Strict as HM
import Data.Text(Text, unpack)
import qualified Data.Text.Lazy as LT
import Data.Text.Lazy.Encoding(decodeUtf8)
import qualified Data.Vector as V
data Notification :: * where
Notification :: Text -> Args -> Notification
deriving instance Show Notification
data Method :: * -> * where
Method :: FromJSON a => Text -> Args -> Method a
deriving instance Show (Method a)
data JSONCall :: * where
NotificationCall :: Notification -> JSONCall
MethodCall :: ToJSON a => Method a -> Value -> JSONCall
type IDTag = Int
type Replies = HM.HashMap IDTag Value
mkMethodCall :: Method a -> IDTag -> JSONCall
mkMethodCall (Method nm args) tag = MethodCall (Method nm args :: Method Value) (Number (fromIntegral tag))
parseReply :: Monad m => Value -> m Replies
parseReply v = case fromJSON v of
Success (rs :: [Value]) -> return $ results rs
_ -> return $ results [v]
where
results :: [Value] -> HM.HashMap IDTag Value
results rs = foldl (\ acc v1 ->
case fromJSON v1 of
Success (Response v2 tag) ->
case fromJSON tag of
Success t -> HM.insert t v2 acc
_ -> error "ParseReply : Unable to obtain tag "
Success (ErrorResponse msg tag) ->
case fromJSON tag of
Success t -> HM.insert t (error $ show msg) acc
_ -> error "ParseReply : Unable to obtain error tag "
) HM.empty rs
parseMethodResult :: (Monad m) => Method a -> IDTag -> Replies -> m a
parseMethodResult (Method {}) tag hm = case HM.lookup tag hm of
Just x -> case fromJSON x of
(Success v) -> return v
_ -> fail $ "bad packet in parseMethodResult:" ++ show x
Nothing -> fail $ "Invalid id lookup in parseMethodResult:" ++ show tag
instance Show JSONCall where
show (MethodCall (Method nm args) tag) = unpack nm ++ show args ++ "#" ++ LT.unpack (decodeUtf8 (encode tag))
show (NotificationCall (Notification nm args)) = unpack nm ++ show args
instance ToJSON JSONCall where
toJSON (MethodCall (Method nm args) tag) = object $
[ "jsonrpc" .= ("2.0" :: Text)
, "method" .= nm
, "id" .= tag
] ++ case args of
None -> []
_ -> [ "params" .= args ]
toJSON (NotificationCall (Notification nm args)) = object $
[ "jsonrpc" .= ("2.0" :: Text)
, "method" .= nm
] ++ case args of
None -> []
_ -> [ "params" .= args ]
instance FromJSON JSONCall where
parseJSON (Object o) =
((\ nm args tag -> MethodCall (Method nm args :: Method Value) tag)
<$> o .: "method"
<*> (o .: "params" <|> return None)
<*> o .: "id") <|>
((\ nm args -> NotificationCall (Notification nm args))
<$> o .: "method"
<*> (o .: "params" <|> return None))
parseJSON _ = fail "not an Object when parsing a JSONCall Value"
newtype RPC a = RPC (RemoteMonad Notification Method a)
deriving (Functor, Applicative, Monad)
data SendAPI :: * -> * where
Sync :: Value -> SendAPI Value
Async :: Value -> SendAPI ()
deriving instance Show (SendAPI a)
data ReceiveAPI :: * -> * where
Receive :: Value -> ReceiveAPI (Maybe Value)
deriving instance Show (ReceiveAPI a)
newtype Session = Session (RemoteMonad Notification Method :~> IO)
data Args where
List :: [Value] -> Args
Named :: [(Text,Value)] -> Args
None :: Args
instance Show Args where
show (List args) =
if null args
then "()"
else concat [ t : LT.unpack (decodeUtf8 (encode x))
| (t,x) <- ('(':repeat ',') `zip` args
] ++ ")"
show (Named args) =
if null args
then "{}"
else concat [ t : show i ++ ":" ++ LT.unpack (decodeUtf8 (encode v))
| (t,(i,v)) <- ('{':repeat ',') `zip` args
] ++ "}"
show None = ""
instance ToJSON Args where
toJSON (List a) = Array (V.fromList a)
toJSON (Named ivs) = object [ i .= v | (i,v) <- ivs ]
toJSON None = Null
instance FromJSON Args where
parseJSON (Array a) = return $ List (V.toList a)
parseJSON (Object fm) = return $ Named (HM.toList fm)
parseJSON Null = return $ None
parseJSON _ = fail "parsing Args"
newtype Tag = Tag Value deriving Show
instance FromJSON Tag where
parseJSON (Object o) = Tag <$> o .: "id"
parseJSON _ = fail "not an Object when parsing a Tag"
data ErrorMessage = ErrorMessage Int Text
deriving Show
instance ToJSON ErrorMessage where
toJSON (ErrorMessage code msg) = object
[ "code" .= code
, "message" .= msg
]
instance FromJSON ErrorMessage where
parseJSON (Object o) = ErrorMessage
<$> o .: "code"
<*> o .: "message"
parseJSON _ = fail "not an Object when parsing an ErrorMessage"
data Response
= Response Value Value
| ErrorResponse ErrorMessage Value
deriving Show
instance ToJSON Response where
toJSON (Response r theId) = object
[ "jsonrpc" .= ("2.0" :: Text)
, "result" .= r
, "id" .= theId
]
toJSON (ErrorResponse msg theId) = object
[ "jsonrpc" .= ("2.0" :: Text)
, "error" .= msg
, "id" .= theId
]
instance FromJSON Response where
parseJSON (Object o) =
pure Response <* (o .: "jsonrpc" :: Parser String)
<*> o .: "result"
<*> o .: "id"
<|> ErrorResponse <$> o .: "error"
<*> o .: "id"
parseJSON _ = fail "not an Object when parsing an Response"