1 {-# LANGUAGE FlexibleInstances #-} 2 {-# LANGUAGE OverloadedStrings #-} 3 4 module Network.Craze.Types where 5 6 import Control.Exception (SomeException) 7 8 import Data.ByteString (ByteString) 9 import Data.Default.Class (Default, def) 10 import Data.Text (Text) 11 import Network.Curl (CurlOption, CurlResponse_, respBody) 12 13 -- | A 'RacerHandler' is simply a function for transforming a response after it 14 -- is received. The handler is only applied to successful requests before they 15 -- are checked by the 'RacerChecker'. 16 -- 17 -- This is primarily for extracting or parsing a 'CurlResponse_' before doing 18 -- any further work. The type returned by the handler will be used as the 19 -- input of the checker and will be the return type of functions like 20 -- 'raceGet'. 21 -- 22 type RacerHandler headerTy bodyTy a = CurlResponse_ headerTy bodyTy -> IO a 23 24 -- | A function that computes whether or not a result is valid or not. 25 -- 26 -- A racer will discard successful responses it get from its clients if they do 27 -- not pass the checker. 28 -- 29 -- This step allows the racer to potentially discard responses that, while 30 -- technically successful, do not contain the expected result (e.g. APIs that 31 -- return errors as HTTP 200s, rate limitting messages, or unexpected formats). 32 -- 33 type RacerChecker a = a -> Bool 34 35 -- | A provider is simply a factory function for 'ProviderOptions', which are 36 -- used to configure a client. 37 type RacerProvider = IO ProviderOptions 38 39 -- | Configuration used to set up an individual client in the race. 40 data ProviderOptions = ProviderOptions 41 { -- | Options to pass down to Curl. 42 poOptions :: [CurlOption] 43 -- | Number of microseconds to delay the request by. 44 -- 45 -- Delays can be used to give other clients a headstart. This is useful 46 -- in cases were some clients are more costly to use than others (e.g. 47 -- Bandwidth costs, resource usage, etc). 48 -- 49 , poDelay :: Maybe Int 50 -- | A tag to identify this type provider. 51 -- 52 -- Tags are not required to be unique, but they are generally more helpful 53 -- if they are. 54 , poTag :: Text 55 } deriving (Show) 56 57 instance Default ProviderOptions where 58 def = ProviderOptions 59 { poOptions = [] 60 , poDelay = Nothing 61 , poTag = "default" 62 } 63 64 -- | The status of running a single client. 65 data ClientStatus a 66 -- | A successful response (passed the checker). A race will usually only 67 -- have one successful response. 68 = Successful a 69 -- | A response that was received but failed to pass the checker. 70 | Failed a 71 -- | An exception thrown while using the client. 72 | Errored SomeException 73 -- | The operation is still pending, was cancelled, or was never started. 74 | Pending 75 deriving (Show) 76 77 -- | The result of a racing operation. This can be used to collect statistics 78 -- on which providers win more often, etc. 79 data RacerResult a = RacerResult 80 { rrResponse :: Maybe a 81 , rrWinner :: Maybe ProviderOptions 82 , rrProviders :: [RacerProvider] 83 , rrStatuses :: [(Text, ClientStatus a)] 84 } 85 86 -- | A record describing the rules for racing requests. 87 data Racer headerTy bodyTy a = Racer 88 { racerHandler :: RacerHandler headerTy bodyTy a 89 , racerChecker :: RacerChecker a 90 -- | On a `Racer`, each `RaceProvider` represents a separate client 91 -- configuration. When performing a race, each provider will be used to spwan 92 -- a client and perform a request. This allows one to control the number of 93 -- requests performed and with which `CurlOption`s. 94 , racerProviders :: [RacerProvider] 95 -- | When set to `True`, debugging messages will be written to stdout. 96 , racerDebug :: Bool 97 -- | When set to `True`, the Racer will attempt to return the last response 98 -- in the event that all responses failed to pass the checker. This can be 99 -- used for identifying error conditions. 100 , racerReturnLast :: Bool 101 } 102 103 instance Default (Racer [(String,String)] ByteString ByteString) where 104 def = Racer 105 { racerHandler = pure . respBody 106 , racerChecker = const True 107 , racerProviders = [] 108 , racerDebug = False 109 , racerReturnLast = False 110 }