pinch-0.1.0.1: examples/keyvalue/Client.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
module Main (main) where
#if __GLASGOW_HASKELL__ < 709
import Control.Applicative
#endif
import Control.Monad
import Control.Monad.Catch (Exception, throwM)
import Data.ByteString (ByteString)
import Data.ByteString.Lazy (toStrict)
import Data.Char (isSpace)
import Data.Function (fix)
import Data.Maybe
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Data.Typeable (Typeable)
import qualified Data.Text as T
import qualified Data.Text.IO as TIO
import qualified Network.HTTP.Client as HTTP
import qualified Network.HTTP.Types as HTTP
import qualified Pinch as P
import Types
data ClientError
= HTTPError HTTP.Status ByteString
| ThriftProtocolError String
deriving (Show, Eq, Typeable)
instance Exception ClientError
keyValueClient :: String -> HTTP.Manager -> KeyValue IO
keyValueClient url manager = KeyValue
{ getValue = mkRequest "getValue"
, setValue = mkRequest "setValue"
}
where
baseRequest = fromMaybe (error "Invalid URL") (HTTP.parseUrl url)
encode = P.encodeMessage P.binaryProtocol
decode = P.decodeMessage P.binaryProtocol
mkRequest :: forall req res.
( P.Pinchable req, P.Tag req ~ P.TStruct
, P.Pinchable res, P.Tag res ~ P.TStruct
) => Text -> req -> IO res
mkRequest name call = do
res <- HTTP.httpLbs req manager
let status = HTTP.responseStatus res
body = toStrict (HTTP.responseBody res)
unless (HTTP.statusIsSuccessful status) $
throwM (HTTPError status body)
case decode body >>= P.getMessageBody of
Left err -> throwM (ThriftProtocolError err)
Right reply -> return reply
where
message = P.mkMessage name P.Call 0 call
req = baseRequest
{ HTTP.method = "POST"
, HTTP.requestBody = HTTP.RequestBodyBS $ encode message
}
main :: IO ()
main = do
client <- keyValueClient "http://localhost:8080"
<$> HTTP.newManager HTTP.defaultManagerSettings
fix $ \loop -> do
line <- TIO.getLine
case T.split isSpace line of
["quit"] -> return ()
["get", key] -> do
res <- getValue client (GetValueRequest (P.putField key))
case res of
GetValueSuccess value -> print value
GetValueDoesNotExist _ -> putStrLn "No matching value."
loop
["set", key, value] -> do
let v = ValueBin (P.putField (encodeUtf8 value))
_ <- setValue client $
SetValueRequest (P.putField key) (P.putField v)
loop
_ -> putStrLn "Invalid command" >> loop