{-# LANGUAGE ViewPatterns #-} -- |Implementations shared across two or more modules. module Cryptsy.API.Public.Internal where -- HTTP import Network.Browser (request) import Network.HTTP.Base (defaultGETRequest_, rspBody, rspCode) -- aeson import Data.Aeson (Value(Object), eitherDecode, withObject) import Data.Aeson.Types (Parser, parseEither) -- either import Control.Monad.Trans.Either (hoistEither, left, right) import Data.Either.Combinators (mapLeft) -- errors import Control.Error.Util ((??)) -- network import Network.URI (parseAbsoluteURI) -- text import Data.Text (Text, pack) -- transformers import Control.Monad.Trans.Class (lift) -- unordered-containers import qualified Data.HashMap.Strict as HM (lookup) -- this package import Cryptsy.API.Public.Types.Error import Cryptsy.API.Public.Types.Monad -- |generates public API URL pubURL :: String -- ^ method value -> String -- ^ complete URL pubURL = ("http://pubapi.cryptsy.com/api.php?method=" ++) {-# INLINABLE pubURL #-} -- |unpacked dataKey dataStr :: String dataStr = "return" -- |key in JSON object for return data dataKey :: Text dataKey = pack dataStr -- |key in JSON object for error message errMsgKey :: Text errMsgKey = pack "error" -- |common request implementation pubCryptsy :: String -- ^ URL -> (Value -> Parser a) -> PubCryptsy a pubCryptsy apiurl parser = do uri <- parseAbsoluteURI apiurl ?? BadURL apiurl let req = defaultGETRequest_ uri (_, resp) <- lift $ request req bodyBytes <- case rspCode resp of (2, 0, 0) -> right $ rspBody resp _ -> left $ BadResponse resp valueJSON <- hoistEither . mapLeft (FailParseResponse bodyBytes) $ eitherDecode bodyBytes returnData <- case valueJSON of Object (HM.lookup dataKey -> Just dat) -> right dat Object (HM.lookup errMsgKey -> Just errMsg) -> left $ ErrorResponse errMsg _ -> left $ UnsuccessfulResponse valueJSON hoistEither . mapLeft (FailParseReturn returnData) $ parseEither parser returnData {-# INLINABLE pubCryptsy #-} -- |unpacked 'marketsKey' marketsStr :: String marketsStr = "markets" -- |failure message when 'marketsKey' is missing missingMsg :: String missingMsg = "Missing '" ++ marketsStr ++ "' key." -- |key in JSON object for market data marketsKey :: Text marketsKey = pack marketsStr -- |Apply a parser on the 'marketsKey' of an object. If not an object or the -- key is missing, fail. onMarkets :: (Value -> Parser a) -> Value -> Parser a onMarkets parser = withObject marketsStr $ maybe (fail missingMsg) parser . HM.lookup marketsKey {-# INLINABLE onMarkets #-}