{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedStrings #-} module Network.Craze.Types where import Control.Exception (SomeException) import Data.ByteString (ByteString) import Data.Default.Class (Default, def) import Data.Text (Text) import Network.Curl (CurlOption, CurlResponse_, respBody) -- | A 'RacerHandler' is simply a function for transforming a response after it -- is received. The handler is only applied to successful requests before they -- are checked by the 'RacerChecker'. -- -- This is primarily for extracting or parsing a 'CurlResponse_' before doing -- any further work. The type returned by the handler will be used as the -- input of the checker and will be the return type of functions like -- 'raceGet'. -- type RacerHandler headerTy bodyTy a = CurlResponse_ headerTy bodyTy -> IO a -- | A function that computes whether or not a result is valid or not. -- -- A racer will discard successful responses it get from its clients if they do -- not pass the checker. -- -- This step allows the racer to potentially discard responses that, while -- technically successful, do not contain the expected result (e.g. APIs that -- return errors as HTTP 200s, rate limitting messages, or unexpected formats). -- type RacerChecker a = a -> Bool -- | A provider is simply a factory function for 'ProviderOptions', which are -- used to configure a client. type RacerProvider = IO ProviderOptions -- | Configuration used to set up an individual client in the race. data ProviderOptions = ProviderOptions { -- | Options to pass down to Curl. poOptions :: [CurlOption] -- | Number of microseconds to delay the request by. -- -- Delays can be used to give other clients a headstart. This is useful -- in cases were some clients are more costly to use than others (e.g. -- Bandwidth costs, resource usage, etc). -- , poDelay :: Maybe Int -- | A tag to identify this type provider. -- -- Tags are not required to be unique, but they are generally more helpful -- if they are. , poTag :: Text } deriving (Show) instance Default ProviderOptions where def = ProviderOptions { poOptions = [] , poDelay = Nothing , poTag = "default" } -- | The status of running a single client. data ClientStatus a -- | A successful response (passed the checker). A race will usually only -- have one successful response. = Successful a -- | A response that was received but failed to pass the checker. | Failed a -- | An exception thrown while using the client. | Errored SomeException -- | The operation is still pending, was cancelled, or was never started. | Pending deriving (Show) -- | The result of a racing operation. This can be used to collect statistics -- on which providers win more often, etc. data RacerResult a = RacerResult { rrResponse :: Maybe a , rrWinner :: Maybe ProviderOptions , rrProviders :: [RacerProvider] , rrStatuses :: [(Text, ClientStatus a)] } -- | A record describing the rules for racing requests. data Racer headerTy bodyTy a = Racer { racerHandler :: RacerHandler headerTy bodyTy a , racerChecker :: RacerChecker a -- | On a `Racer`, each `RaceProvider` represents a separate client -- configuration. When performing a race, each provider will be used to spwan -- a client and perform a request. This allows one to control the number of -- requests performed and with which `CurlOption`s. , racerProviders :: [RacerProvider] -- | When set to `True`, debugging messages will be written to stdout. , racerDebug :: Bool -- | When set to `True`, the Racer will attempt to return the last response -- in the event that all responses failed to pass the checker. This can be -- used for identifying error conditions. , racerReturnLast :: Bool } instance Default (Racer [(String,String)] ByteString ByteString) where def = Racer { racerHandler = pure . respBody , racerChecker = const True , racerProviders = [] , racerDebug = False , racerReturnLast = False }