{-# LANGUAGE TypeApplications #-} module Gargoyle.PostgreSQL.Connect (withDb, withDb', openDb) where import Control.Monad ((>=>)) import Data.ByteString (ByteString) import qualified Data.ByteString.Char8 as C8 import Data.Pool (Pool, defaultPoolConfig, newPool, setNumStripes) import Database.PostgreSQL.Simple (Connection, close, connectPostgreSQL) import Gargoyle (withGargoyle) import Gargoyle.PostgreSQL.Nix (postgresNix) import System.Directory (doesFileExist) -- | Like 'withDb'' but turns the connection string into a connection -- 'Pool' for you and using 'error' on failure. withDb :: String -> (Pool Connection -> IO a) -> IO a withDb dbPath f = either error pure =<< withDb' dbPath (openDb >=> f) -- | Convert a connection string into a connection 'Pool'. openDb :: ByteString -> IO (Pool Connection) openDb dbUri = newPool $ setNumStripes (Just 1) $ defaultPoolConfig (connectPostgreSQL dbUri) close (realToFrac @Int 5) 20 -- | Connects to a database using information at the given filepath. -- The given filepath can be either a folder (for a local db) -- or a file with a database connection string. -- -- 'withDb'' takes a String, which represents the path to a database, and a -- function that returns database connection information as arguments in -- order to open and start the database. Otherwise, it will create the -- database for you if it doesn't exist. withDb' :: String -> (ByteString -> IO a) -> IO (Either String a) withDb' dbPath f = do dbExists <- doesFileExist dbPath if dbExists -- use the file contents as the URI for an existing server then C8.readFile dbPath >>= \b -> case C8.lines b of [] -> pure $ Left "DB connection string configuration file is empty" -- TODO: Consider also blowing up if more than one line for next breaking release x:_ -> Right <$> f x -- otherwise assume it's a folder for a local database else do g <- postgresNix Right <$> withGargoyle g dbPath f