{-# LANGUAGE DataKinds #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications #-}
module Arbor.Postgres.Core where
import Arbor.Postgres.Password
import Control.Exception (catch, throw)
import Control.Lens
import Control.Monad (void)
import Data.ByteString
import Data.Generics.Product.Any
import Data.Int
import Data.Monoid ((<>))
import Data.String
import Network.URI
import qualified Arbor.Postgres.Config as Z
import qualified Arbor.Postgres.Env as E
import qualified Data.Text as T
import qualified Database.PostgreSQL.Simple as PGS
parseConfig :: Z.PostgresConfig -> ByteString
parseConfig = PGS.postgreSQLConnectionString . Z.configToConnectInfo
createDatabaseStatement :: T.Text -> PGS.Query
createDatabaseStatement db = fromString . T.unpack $ "CREATE DATABASE \"" <> db <> "\""
createDatabase :: Z.PostgresConfig -> IO ()
createDatabase postgresConfig = do
conn <- PGS.connectPostgreSQL $ parseConfig $ postgresConfig { Z.database = "postgres" }
let q = createDatabaseStatement (postgresConfig ^. the @"database")
void $ PGS.execute_ conn q `catch` duplicateDatabase
duplicateDatabase :: PGS.SqlError -> IO Int64
duplicateDatabase e =
if PGS.sqlState e == "42P04"
then return 0
else throw e
connectPostgres :: Z.PostgresConfig -> IO E.PostgresEnv
connectPostgres postgresConfig = do
conn <- PGS.connectPostgreSQL $ parseConfig postgresConfig
return $ E.PostgresEnv conn (mkConnectionString postgresConfig)
newtype Table = Table
{ table :: T.Text }
deriving (IsString, Show)
mkConnectionString :: Z.PostgresConfig -> URI
mkConnectionString config = do
let host = config ^. the @"host" & T.unpack
let dbname = config ^. the @"database" & T.unpack
let auth = pure $ URIAuth "" host ":5432"
let q = ""
let frag = ""
URI "postgresql:" auth ("/" <> dbname) q frag
mkResourceURI :: URI -> Table -> [(T.Text, T.Text)] -> URI
mkResourceURI uri (Table tbl) kvs = do
let q = "?" <> T.intercalate "&" (uncurry (\k v -> k <> "=" <> v) <$> (("table", tbl) : kvs)) & T.unpack
uri { uriQuery = q }