module System.Environment.Parser.Database (
DBConnection (..), Provider (..), providerString
) where
import Control.Applicative
import qualified Data.Attoparsec.Text as At
import qualified Data.ByteString as S
import qualified Data.ByteString.Char8 as S8
import qualified Data.Map as Map
import qualified Data.Text as T
import qualified Data.Text.Encoding as TE
import qualified Network.HTTP.Types as Ht
import qualified Network.URI as URI
import System.Environment.Parser.FromEnv
data Provider = Postgres S.ByteString
| MySQL S.ByteString
| AMQP S.ByteString
| HTTP S.ByteString
| Other S.ByteString
providerString :: Provider -> S.ByteString
providerString p = case p of
Postgres s -> s
MySQL s -> s
AMQP s -> s
HTTP s -> s
Other s -> s
data DBConnection = DBConnection
{ provider :: Provider
, host :: S.ByteString
, username :: S.ByteString
, password :: S.ByteString
, port :: Int
, location :: S.ByteString
, params :: Map.Map S.ByteString S.ByteString
}
instance FromEnv DBConnection where
parseEnv = tryParse
tryParse :: String -> Either String DBConnection
tryParse s = do
uri <- e "invalid URI format" $ URI.parseAbsoluteURI s
auth <- e "URI authority segment missing" $ URI.uriAuthority uri
port <- parsePort (URI.uriPort auth)
(username, password) <- parseUserPw (URI.uriUserInfo auth)
return DBConnection
{ provider = guessProvider (URI.uriScheme uri)
, host = S8.pack (URI.uriRegName auth)
, username = username
, password = password
, port = port
, location = S8.pack $ drop 1 $ URI.uriPath uri
, params = makeQueryMap (URI.uriQuery uri)
}
where
guessProvider :: String -> Provider
guessProvider x = case x of
"postgres:" -> Postgres bs
"mysql:" -> MySQL bs
"mysql2:" -> MySQL bs
"http:" -> HTTP bs
"amqp:" -> AMQP bs
_ -> Other bs
where bs = S8.pack s
parsePort :: String -> Either String Int
parsePort = At.parseOnly (At.char ':' *> At.decimal) . T.pack
parseUserPw :: String -> Either String (S.ByteString, S.ByteString)
parseUserPw = At.parseOnly ( (,) <$> (TE.encodeUtf8 <$> At.takeTill (==':') <* At.char ':')
<*> (TE.encodeUtf8 <$> At.takeTill (=='@') <* At.char '@') )
. T.pack
makeQueryMap :: String -> Map.Map S.ByteString S.ByteString
makeQueryMap = Map.fromList . Ht.parseSimpleQuery . S8.pack
e :: String -> Maybe a -> Either String a
e s Nothing = Left s
e _ (Just a) = Right a