{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE OverloadedStrings #-}
module Network.HTTP.Client.Overrides.Internal.Types
( ConfigFile(..)
, Config(..)
, LogOptions(..)
, LogFormat(..)
, RequestOverride(..)
, URL(..)
, parseURL
) where
import Data.Either (either)
import Data.List (intercalate)
import Data.Char (toLower)
import GHC.Generics
import Text.Read (readMaybe)
import Data.Aeson (FromJSON, Value(..), (.:), (.:?), (.!=), parseJSON, withObject, withText)
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Char8 as BS
import Data.Text (Text)
import qualified Data.Text as T
import Network.URI (URI)
import qualified Network.URI as URI
data ConfigFile
= V1 Config
deriving (Show, Eq, Generic)
instance FromJSON ConfigFile where
parseJSON = withObject "ConfigFile" $ \o -> do
version <- o .: "version"
case version of
"v1" -> V1 <$> parseJSON (Object o)
_ -> fail $ "Version '" <> version <> "' not supported"
data Config = Config
{ logOptions :: !LogOptions
, requestOverrides :: ![RequestOverride]
} deriving (Show, Eq, Generic)
instance FromJSON Config where
parseJSON = withObject "Config" $ \o -> Config
<$> o .:? "logOptions" .!= LogOptions Nothing Nothing Nothing
<*> o .:? "requestOverrides" .!= []
data LogOptions = LogOptions
{ logResponses :: !(Maybe LogFormat)
, logRequests :: !(Maybe LogFormat)
, logRequestOverrides :: !(Maybe LogFormat)
} deriving (Show, Eq, Generic)
instance FromJSON LogOptions where
parseJSON = Aeson.genericParseJSON opts
where
opts = Aeson.defaultOptions { Aeson.fieldLabelModifier = toLowerHead . drop 3 }
toLowerHead [] = []
toLowerHead (x:xs) = (toLower x):xs
data LogFormat
= Simple
| Detailed
deriving (Show, Eq, Generic)
instance FromJSON LogFormat where
parseJSON = withText "LogFormat" $ \t -> case T.toLower t of
"simple" -> pure $ Simple
"detailed" -> pure $ Detailed
_ -> fail $ "Unknown LogFormat: " <> T.unpack t
data RequestOverride = RequestOverride
{ match :: !URL
, override :: !URL
} deriving (Eq, Generic)
instance Show RequestOverride where
show x = intercalate "\n" $
[ "RequestOverride {"
, " match ="
]
++
(map (" " ++) . lines . show $ match x)
++
[ " override ="
]
++
(map (" " ++) . lines . show $ override x)
++
[ "}"
]
instance FromJSON RequestOverride
data URL = URL
{ secure :: !(Maybe Bool)
, host :: !(Maybe BS.ByteString)
, port :: !(Maybe Int)
, path :: !(Maybe BS.ByteString)
} deriving (Eq, Generic)
instance Show URL where
show x = intercalate "\n" $
[ "URL {"
, " secure = " ++ show (secure x)
, " host = " ++ show (host x)
, " port = " ++ show (port x)
, " path = " ++ show (path x)
, "}"
]
instance FromJSON URL where
parseJSON = withText "URL" $ either fail return . parseURL
parseURL :: Text -> Either String URL
parseURL url
| "http://" `T.isPrefixOf` url = toURL (Just False) url
| "https://" `T.isPrefixOf` url = toURL (Just True) url
| "://" `T.isInfixOf` url = Left $ "Unspported scheme: " ++ T.unpack url
| otherwise = toURL Nothing $ "https://" <> url
toURL :: Maybe Bool -> Text -> Either String URL
toURL https url = do
uri <- case URI.parseURIReference $ T.unpack url of
Nothing -> Left $ "Failed to parse URL: " ++ T.unpack url
Just x -> Right x
p <- getPort uri
return $ URL https (getHost uri) p (getPath uri)
getHost :: URI -> Maybe BS.ByteString
getHost uri = do
authority <- URI.uriAuthority uri
case URI.uriRegName authority of
"" -> Nothing
x -> Just $ BS.pack x
getPort :: URI -> Either String (Maybe Int)
getPort uri =
case fmap URI.uriPort $ URI.uriAuthority uri of
Just "" ->
Right Nothing
Just ":" ->
Right Nothing
Just (':':x) ->
case readMaybe x of
Nothing -> Left $ "Failed to parse Int: " ++ show x
Just p -> Right $ Just p
_ ->
Right Nothing
getPath :: URI -> Maybe BS.ByteString
getPath uri = case URI.uriPath uri of
"" -> Nothing
x -> Just $ BS.pack x