clplug-1.0.0.0: src/Plugin/Internal/Conduit.hs
{-# LANGUAGE
LambdaCase
, FlexibleInstances
, FlexibleContexts
, DeriveGeneric
, OverloadedStrings
#-}
module Plugin.Internal.Conduit (
inConduit,
ParseResult(..),
Res(..),
Req(..)
) where
import GHC.Generics
import Data.Text (Text)
import Control.Applicative ((<|>))
import Control.Monad.State.Lazy
import Data.Aeson.Types hiding ( parse )
import Data.Aeson
import Data.Aeson.Parser (json')
import Data.Conduit
import Data.ByteString (ByteString)
import Data.Attoparsec.ByteString as A
import Control.Monad
-- | Decode from bytestring into a JSON object. inspired by package json-rpc
inConduit :: (Monad n, FromJSON a) =>
ConduitT ByteString (ParseResult a) n ()
inConduit = evalStateT (l) Nothing
where
l = lift await >>= maybe (error "non") p
p = r >=> h
r i = get >>= \case
Nothing -> pure $ parse json' i
Just k -> pure $ k i
h = \case
Fail{} -> lift (yield ParseErr) >> put Nothing >> l
Partial i -> put (Just i) >> l
Done rest v -> do
lift . yield . maybe InvalidReq Correct $ parseMaybe parseJSON v
if rest == "\n\n" then put Nothing >> l else p rest
data ParseResult x =
Correct !x |
InvalidReq |
ParseErr
deriving (Show, Generic)
instance ToJSON a => ToJSON (ParseResult a) where
toJSON = genericToJSON defaultOptions
instance FromJSON a => FromJSON (ParseResult a)
data Req = Req {
reqId :: Maybe Int,
reqMethod :: Text,
reqParams :: Value
} deriving (Show)
data Res =
Res { resBody :: Value,
resId :: Int }
| ErrRes {
errMsg :: Text,
errCode :: Int,
errData :: Maybe Value,
errId :: Int }
deriving (Show, Generic)
instance FromJSON Req where
parseJSON (Object v) = do
version <- v .: "jsonrpc"
guard (version == ("2.0" :: Text))
Req <$> v .:? "id"
<*> v .: "method"
<*> (v .:? "params") .!= emptyArray
parseJSON _ = mempty
instance FromJSON Res where
parseJSON (Object v) = do
version <- v .: "jsonrpc"
guard (version == ("2.0" :: Text))
fromResult <|> fromError
where
fromResult = Res <$> v .: "result"
<*> v .: "id"
fromError = do
err <- v .: "error"
ErrRes <$> err .: "message"
<*> err .: "code"
<*> err .:? "data"
<*> v .: "id"
parseJSON (Array _) = mempty
parseJSON _ = mempty
instance ToJSON (Req) where
toJSON (Req i m ps ) =
object [ "jsonrpc" .= ("2.0" :: Text)
, "method" .= m
, "params" .= ps
, "id" .= i ]
instance ToJSON Res where
toJSON (Res x i) = object [
"jsonrpc" .= ("2.0" :: Text),
"result" .= x,
"id" .= i ]
toJSON (ErrRes msg c d i) = object [
"jsonrpc" .= ("2.0" :: Text),
"error" .= object ["message" .= msg,
"code" .= c,
"data" .= d
],
"id" .= i ]