hasql-url-0.1.0.0: src/Hasql/URL.hs
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
-- |
-- Module : Hasql.URL
-- Description : Parses a postgresql connection string into a Hasql Settings
-- License : MIT
--
-- Maintainer : Nadeem Bitar <nadeem@gmail.com>
--
--
-- This is a direct port of [postgresql-simple-url](https://hackage.haskell.org/package/postgresql-simple-url) to Hasql.
module Hasql.URL
( parseDatabaseUrl,
)
where
import Data.ByteString (ByteString)
import Data.ByteString.Char8 (pack)
import Data.List.Split
import Data.Word (Word16)
import Hasql.Connection (Settings, settings)
import Network.URI
data ConnectionInfo = ConnectionInfo
{ _port :: Word16,
_username :: ByteString,
_host :: ByteString,
_password :: ByteString,
_database :: ByteString
}
deriving stock (Show)
defaultConnectionInfo :: ConnectionInfo
defaultConnectionInfo = ConnectionInfo 5432 "postgres" "" "" ""
-- | Parse a string postgresql url into `Hasql.Connection.Settings`
--
-- @
-- parseDatabaseUrl \"postgres://username:password@domain.com:5433/database\" ==
-- Just $ settings \"domain.com\" (fromInteger 5433) \"username\" \"password\" \"database\"
-- @
parseDatabaseUrl :: String -> Maybe Settings
parseDatabaseUrl databaseUrl = parseURI databaseUrl >>= uriToSettings
uriToSettings :: URI -> Maybe Settings
uriToSettings uri
| uriScheme uri /= "postgres:" && uriScheme uri /= "postgresql:" = Nothing
| otherwise = mkSettingsFromConnectionInfo . ($ defaultConnectionInfo) <$> mkConnectionInfo uri
mkSettingsFromConnectionInfo :: ConnectionInfo -> Settings
mkSettingsFromConnectionInfo u = settings (_host u) (_port u) (_username u) (_password u) (_database u)
dropLast :: [a] -> [a]
dropLast [] = []
dropLast l = init l
mkConnectionInfo :: URI -> Maybe (ConnectionInfo -> ConnectionInfo)
mkConnectionInfo uri = case uriPath uri of
('/' : rest) | not (null rest) -> Just $ uriParameters uri
_ -> Nothing
uriParameters :: URI -> (ConnectionInfo -> ConnectionInfo)
uriParameters uri = (\info -> info {_database = pack . tail $ uriPath uri}) . maybe id uriAuthParameters (uriAuthority uri)
uriAuthParameters :: URIAuth -> (ConnectionInfo -> ConnectionInfo)
uriAuthParameters uriAuth = port . host . auth
where
port = case uriPort uriAuth of
(':' : p) -> \i -> i {_port = read p}
_ -> id
host = case uriRegName uriAuth of
h -> \i -> i {_host = pack h}
auth = case splitOn ":" (uriUserInfo uriAuth) of
[""] -> id
[u] -> \i -> i {_username = pack $ dropLast u}
[u, p] -> \i -> i {_username = pack u, _password = pack $ dropLast p}
_ -> id