postgresql-simple-opts 0.3.0.1 → 0.4.0.0
raw patch · 4 files changed
+235/−301 lines, 4 filesdep +postgres-optionsPVP ok
version bump matches the API change (PVP)
Dependencies added: postgres-options
API changes (from Hackage documentation)
- Database.PostgreSQL.Simple.Options: Options :: Maybe String -> Maybe String -> Maybe Int -> Maybe String -> Maybe String -> String -> Maybe Int -> Maybe String -> Maybe String -> Maybe String -> Maybe Int -> Maybe Int -> Maybe Int -> Maybe String -> Maybe Int -> Maybe Int -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Maybe String -> Options
- Database.PostgreSQL.Simple.Options: PartialOptions :: Last String -> Last String -> Last Int -> Last String -> Last String -> Last String -> Last Int -> Last String -> Last String -> Last String -> Last Int -> Last Int -> Last Int -> Last String -> Last Int -> Last Int -> Last String -> Last String -> Last String -> Last String -> Last String -> Last String -> Last String -> PartialOptions
- Database.PostgreSQL.Simple.Options: [clientEncoding] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [connectTimeout] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [dbname] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [fallbackApplicationName] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [gsslib] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [host] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [hostaddr] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [keepalivesCount] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [keepalivesIdle] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [keepalives] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [krbsrvname] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [oClientEncoding] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oConnectTimeout] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oDbname] :: Options -> String
- Database.PostgreSQL.Simple.Options: [oFallbackApplicationName] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oGsslib] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oHost] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oHostaddr] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oKeepalivesCount] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oKeepalivesIdle] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oKeepalives] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oKrbsrvname] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oOptions] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oPassword] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oPort] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oRequirepeer] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oRequiressl] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oService] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oSslcert] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oSslcompression] :: Options -> Maybe Int
- Database.PostgreSQL.Simple.Options: [oSslkey] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oSslmode] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oSslrootcert] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [oUser] :: Options -> Maybe String
- Database.PostgreSQL.Simple.Options: [options] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [password] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [port] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [requirepeer] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [requiressl] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [service] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [sslcert] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [sslcompression] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [sslkey] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [sslmode] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [sslrootcert] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [user] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: autorityToPartialOptions :: Authority -> PartialOptions
- Database.PostgreSQL.Simple.Options: completeOptions :: PartialOptions -> Either [String] Options
- Database.PostgreSQL.Simple.Options: completeParser :: Parser Options
- Database.PostgreSQL.Simple.Options: data Options
- Database.PostgreSQL.Simple.Options: data PartialOptions
- Database.PostgreSQL.Simple.Options: getLast' :: Applicative f => Last a -> f (Maybe a)
- Database.PostgreSQL.Simple.Options: getOption :: String -> Last a -> Validation [String] a
- Database.PostgreSQL.Simple.Options: instance Data.Default.Class.Default Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Base.Monoid Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Base.Semigroup Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Eq Database.PostgreSQL.Simple.Options.Options
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Eq Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Ord Database.PostgreSQL.Simple.Options.Options
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Ord Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Generics.Generic Database.PostgreSQL.Simple.Options.Options
- Database.PostgreSQL.Simple.Options: instance GHC.Generics.Generic Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Read.Read Database.PostgreSQL.Simple.Options.Options
- Database.PostgreSQL.Simple.Options: instance GHC.Read.Read Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance GHC.Show.Show Database.PostgreSQL.Simple.Options.Options
- Database.PostgreSQL.Simple.Options: instance GHC.Show.Show Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: instance Options.Generic.ParseRecord Database.PostgreSQL.Simple.Options.PartialOptions
- Database.PostgreSQL.Simple.Options: keywordToPartialOptions :: String -> String -> Either String PartialOptions
- Database.PostgreSQL.Simple.Options: maybeToPair :: Show a => String -> Maybe a -> [(String, String)]
- Database.PostgreSQL.Simple.Options: maybeToPairStr :: String -> Maybe String -> [(String, String)]
- Database.PostgreSQL.Simple.Options: mkLast :: a -> Last a
- Database.PostgreSQL.Simple.Options: parseConnectionString :: String -> Either String PartialOptions
- Database.PostgreSQL.Simple.Options: parseInt :: String -> String -> Either String Int
- Database.PostgreSQL.Simple.Options: parseKeywords :: String -> Either String PartialOptions
- Database.PostgreSQL.Simple.Options: parseString :: String -> Maybe String
- Database.PostgreSQL.Simple.Options: parseURIStr :: String -> Either String (URIRef Absolute)
- Database.PostgreSQL.Simple.Options: pathToPartialOptions :: ByteString -> PartialOptions
- Database.PostgreSQL.Simple.Options: queryToPartialOptions :: Query -> Either String PartialOptions
- Database.PostgreSQL.Simple.Options: run :: Options -> IO Connection
- Database.PostgreSQL.Simple.Options: toArgs :: Options -> [String]
- Database.PostgreSQL.Simple.Options: toConnectionString :: Options -> ByteString
- Database.PostgreSQL.Simple.Options: unSingleQuote :: String -> Maybe String
- Database.PostgreSQL.Simple.Options: underscoreModifiers :: Modifiers
- Database.PostgreSQL.Simple.Options: uriToOptions :: URIRef Absolute -> Either String PartialOptions
- Database.PostgreSQL.Simple.Options: userInfoToPartialOptions :: UserInfo -> PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: PartialOptions :: Last String -> Last String -> Last Int -> Last String -> Last String -> Last String -> Last Int -> Last String -> Last String -> Last String -> Last Int -> Last Int -> Last Int -> Last String -> Last Int -> Last Int -> Last String -> Last String -> Last String -> Last String -> Last String -> Last String -> Last String -> PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: [clientEncoding] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [connectTimeout] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [dbname] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [fallbackApplicationName] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [gsslib] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [host] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [hostaddr] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [keepalivesCount] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [keepalivesIdle] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [keepalives] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [krbsrvname] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [options] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [password] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [port] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [requirepeer] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [requiressl] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [service] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [sslcert] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [sslcompression] :: PartialOptions -> Last Int
+ Database.PostgreSQL.Simple.PartialOptions: [sslkey] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [sslmode] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [sslrootcert] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: [user] :: PartialOptions -> Last String
+ Database.PostgreSQL.Simple.PartialOptions: autorityToPartialOptions :: Authority -> PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: completeOptions :: PartialOptions -> Either [String] Options
+ Database.PostgreSQL.Simple.PartialOptions: completeParser :: Parser Options
+ Database.PostgreSQL.Simple.PartialOptions: data PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: getLast' :: Applicative f => Last a -> f (Maybe a)
+ Database.PostgreSQL.Simple.PartialOptions: getOption :: String -> Last a -> Validation [String] a
+ Database.PostgreSQL.Simple.PartialOptions: instance Data.Default.Class.Default Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Base.Monoid Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Base.Semigroup Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Classes.Eq Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Classes.Ord Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Generics.Generic Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Read.Read Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance GHC.Show.Show Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: instance Options.Generic.ParseRecord Database.PostgreSQL.Simple.PartialOptions.PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: keywordToPartialOptions :: String -> String -> Either String PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: mkLast :: a -> Last a
+ Database.PostgreSQL.Simple.PartialOptions: parseConnectionString :: String -> Either String PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: parseInt :: String -> String -> Either String Int
+ Database.PostgreSQL.Simple.PartialOptions: parseKeywords :: String -> Either String PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: parseString :: String -> Maybe String
+ Database.PostgreSQL.Simple.PartialOptions: parseURIStr :: String -> Either String (URIRef Absolute)
+ Database.PostgreSQL.Simple.PartialOptions: pathToPartialOptions :: ByteString -> PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: queryToPartialOptions :: Query -> Either String PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: run :: Options -> IO Connection
+ Database.PostgreSQL.Simple.PartialOptions: unSingleQuote :: String -> Maybe String
+ Database.PostgreSQL.Simple.PartialOptions: underscoreModifiers :: Modifiers
+ Database.PostgreSQL.Simple.PartialOptions: uriToOptions :: URIRef Absolute -> Either String PartialOptions
+ Database.PostgreSQL.Simple.PartialOptions: userInfoToPartialOptions :: UserInfo -> PartialOptions
Files
- postgresql-simple-opts.cabal +4/−2
- src/Database/PostgreSQL/Simple/Options.hs +0/−298
- src/Database/PostgreSQL/Simple/PartialOptions.hs +230/−0
- test/Spec.hs +1/−1
postgresql-simple-opts.cabal view
@@ -1,5 +1,5 @@ name: postgresql-simple-opts-version: 0.3.0.1+version: 0.4.0.0 synopsis: An optparse-applicative parser for postgresql-simple's connection options description: This package exports a optparse-applicative parser and type for postgresql-simple's Options and connection string. homepage: https://github.com/jfischoff/postgresql-simple-opts#readme@@ -15,7 +15,7 @@ library hs-source-dirs: src- exposed-modules: Database.PostgreSQL.Simple.Options+ exposed-modules: Database.PostgreSQL.Simple.PartialOptions build-depends: base >= 4.6 && < 5 , postgresql-simple , optparse-applicative@@ -26,6 +26,7 @@ , split , uri-bytestring , generic-deriving+ , postgres-options default-language: Haskell2010 ghc-options: -Wall -fno-warn-unused-do-bind@@ -41,6 +42,7 @@ , optparse-applicative , bytestring , data-default+ , postgres-options ghc-options: -Wall -fno-warn-unused-do-bind -threaded
− src/Database/PostgreSQL/Simple/Options.hs
@@ -1,298 +0,0 @@-{-| A resuable optparse-applicative parser for creating a postgresql-simple- 'Connection'--}-{-# LANGUAGE RecordWildCards, LambdaCase, DeriveGeneric, DeriveDataTypeable #-}-{-# LANGUAGE GeneralizedNewtypeDeriving, CPP, GADTs, OverloadedStrings #-}-{-# LANGUAGE TupleSections #-}-module Database.PostgreSQL.Simple.Options where-import Database.PostgreSQL.Simple-import Options.Applicative-import Text.Read-import Data.ByteString (ByteString)-import qualified Data.ByteString.Char8 as BSC-import qualified Data.ByteString as BS-import GHC.Generics-import Options.Generic-import Data.Typeable-import Data.Either.Validation-import Data.Default-import URI.ByteString as URI-import Control.Monad-import Data.List.Split-import Data.List (intercalate)-import Generics.Deriving.Monoid-import Data.Char-import Data.Maybe--data Options = Options- { oHost :: Maybe String- , oHostaddr :: Maybe String- , oPort :: Maybe Int- , oUser :: Maybe String- , oPassword :: Maybe String- , oDbname :: String- , oConnectTimeout :: Maybe Int- , oClientEncoding :: Maybe String- , oOptions :: Maybe String- , oFallbackApplicationName :: Maybe String- , oKeepalives :: Maybe Int- , oKeepalivesIdle :: Maybe Int- , oKeepalivesCount :: Maybe Int- , oSslmode :: Maybe String- , oRequiressl :: Maybe Int- , oSslcompression :: Maybe Int- , oSslcert :: Maybe String- , oSslkey :: Maybe String- , oSslrootcert :: Maybe String- , oRequirepeer :: Maybe String- , oKrbsrvname :: Maybe String- , oGsslib :: Maybe String- , oService :: Maybe String- } deriving (Show, Eq, Read, Ord, Generic, Typeable)--- | An optional version of 'Options'. This includes an instance of--- | 'ParseRecord' which provides the optparse-applicative Parser.-data PartialOptions = PartialOptions- { host :: Last String- , hostaddr :: Last String- , port :: Last Int- , user :: Last String- , password :: Last String- , dbname :: Last String- , connectTimeout :: Last Int- , clientEncoding :: Last String- , options :: Last String- , fallbackApplicationName :: Last String- , keepalives :: Last Int- , keepalivesIdle :: Last Int- , keepalivesCount :: Last Int- , sslmode :: Last String- , requiressl :: Last Int- , sslcompression :: Last Int- , sslcert :: Last String- , sslkey :: Last String- , sslrootcert :: Last String- , requirepeer :: Last String- , krbsrvname :: Last String- , gsslib :: Last String- , service :: Last String- } deriving (Show, Eq, Read, Ord, Generic, Typeable)--instance ParseRecord PartialOptions where- parseRecord = (option (eitherReader parseConnectionString) (long "connectString"))- <|> parseRecordWithModifiers defaultModifiers--instance Semigroup PartialOptions where- (<>) = gmappenddefault--instance Monoid PartialOptions where- mempty = gmemptydefault- mappend = (<>)---- Copied from Options.Generic source code-underscoreModifiers :: Modifiers-underscoreModifiers = Modifiers lispCase lispCase (const Nothing)- where- lispCase = dropWhile (== '_') . (>>= lower) . dropWhile (== '_')- lower c | isUpper c = ['_', toLower c]- | otherwise = [c]---unSingleQuote :: String -> Maybe String-unSingleQuote (x : xs@(_ : _))- | x == '\'' && last xs == '\'' = Just $ init xs- | otherwise = Nothing-unSingleQuote _ = Nothing--parseString :: String -> Maybe String-parseString x = readMaybe x <|> unSingleQuote x <|> Just x--mkLast :: a -> Last a-mkLast = Last . Just---- | The 'PartialOptions' version of 'defaultOptions'-instance Default PartialOptions where- def = mempty- { host = mkLast $ connectHost defaultConnectInfo- , port = mkLast $ fromIntegral $ connectPort defaultConnectInfo- , user = mkLast $ connectUser defaultConnectInfo- , password = mkLast $ connectPassword defaultConnectInfo- , dbname = mkLast $ connectDatabase defaultConnectInfo- }--getOption :: String -> Last a -> Validation [String] a-getOption optionName = \case- Last (Just x) -> pure x- Last Nothing -> Data.Either.Validation.Failure- ["Missing " ++ optionName ++ " option"]--getLast' :: Applicative f => Last a -> f (Maybe a)-getLast' = pure . getLast--completeOptions :: PartialOptions -> Either [String] Options-completeOptions PartialOptions {..} = validationToEither $ do- Options <$> getLast' host- <*> getLast' hostaddr- <*> (fmap fromIntegral <$> getLast' port)- <*> getLast' user- <*> getLast' password- <*> getOption "dbname" dbname- <*> getLast' connectTimeout- <*> getLast' clientEncoding- <*> getLast' options- <*> getLast' fallbackApplicationName- <*> getLast' keepalives- <*> getLast' keepalivesIdle- <*> getLast' keepalivesCount- <*> getLast' sslmode- <*> getLast' requiressl- <*> getLast' sslcompression- <*> getLast' sslcert- <*> getLast' sslkey- <*> getLast' sslrootcert- <*> getLast' requirepeer- <*> getLast' krbsrvname- <*> getLast' gsslib- <*> getLast' service--maybeToPairStr :: String -> Maybe String -> [(String, String)]-maybeToPairStr k mv = (\v -> (k, v)) <$> maybeToList mv--maybeToPair :: Show a => String -> Maybe a -> [(String, String)]-maybeToPair k mv = (\v -> (k, show v)) <$> maybeToList mv--toConnectionString :: Options -> ByteString-toConnectionString Options {..} = BSC.pack $ unwords $ map (\(k, v) -> k <> "=" <> v)- $ maybeToPairStr "host" oHost- <> maybeToPairStr "hostaddr" oHostaddr- <> [ ("dbname", oDbname)- ]- <> maybeToPair "port" oPort- <> maybeToPairStr "password" oPassword- <> maybeToPairStr "user" oUser- <> maybeToPair "connect_timeout" oConnectTimeout- <> maybeToPairStr "client_encoding" oClientEncoding- <> maybeToPairStr "options" oOptions- <> maybeToPairStr "fallback_applicationName" oFallbackApplicationName- <> maybeToPair "keepalives" oKeepalives- <> maybeToPair "keepalives_idle" oKeepalivesIdle- <> maybeToPair "keepalives_count" oKeepalivesCount- <> maybeToPairStr "sslmode" oSslmode- <> maybeToPair "requiressl" oRequiressl- <> maybeToPair "sslcompression" oSslcompression- <> maybeToPairStr "sslcert" oSslcert- <> maybeToPairStr "sslkey" oSslkey- <> maybeToPairStr "sslrootcert" oSslrootcert- <> maybeToPairStr "requirepeer" oRequirepeer- <> maybeToPairStr "krbsrvname" oKrbsrvname- <> maybeToPairStr "gsslib" oGsslib- <> maybeToPairStr "service" oService----- | Useful for testing or if only Options are needed.-completeParser :: Parser Options-completeParser =- fmap (either (error . unlines) id . completeOptions . mappend def) parseRecord---- | Create a connection with an 'Option'-run :: Options -> IO Connection-run = connectPostgreSQL . toConnectionString--userInfoToPartialOptions :: UserInfo -> PartialOptions-userInfoToPartialOptions UserInfo {..} = mempty { user = return $ BSC.unpack uiUsername } <> if BS.null uiPassword- then mempty- else mempty { password = return $ BSC.unpack uiPassword }--autorityToPartialOptions :: Authority -> PartialOptions-autorityToPartialOptions Authority {..} = maybe mempty userInfoToPartialOptions authorityUserInfo <>- mempty { host = return $ BSC.unpack $ hostBS authorityHost } <>- maybe mempty (\p -> mempty { port = return $ portNumber p }) authorityPort--pathToPartialOptions :: ByteString -> PartialOptions-pathToPartialOptions path = case drop 1 $ BSC.unpack path of- "" -> mempty- x -> mempty {dbname = return x }--parseInt :: String -> String -> Either String Int-parseInt msg v = maybe (Left (msg <> " value of: " <> v <> " is not a number")) Right $- readMaybe v--keywordToPartialOptions :: String -> String -> Either String PartialOptions-keywordToPartialOptions k v = case k of- "host" -> return $ mempty { host = return $ v }- "hostaddress" -> return $ mempty { hostaddr = return $ v }- "port" -> do- portValue <- parseInt "port" v- return $ mempty { port = return portValue }- "user" -> return $ mempty { user = return v }- "password" -> return $ mempty { password = return v }- "dbname" -> return $ mempty { dbname = return v}- "connect_timeout" -> do- x <- parseInt "connect_timeout" v- return $ mempty { connectTimeout = return x }- "client_encoding" -> return $ mempty { clientEncoding = return v }- "options" -> return $ mempty { options = return v }- "fallback_applicationName" -> return $ mempty { fallbackApplicationName = return v }- "keepalives" -> do- x <- parseInt "keepalives" v- return $ mempty { keepalives = return x }- "keepalives_idle" -> do- x <- parseInt "keepalives_idle" v- return $ mempty { keepalivesIdle = return x }- "keepalives_count" -> do- x <- parseInt "keepalives_count" v- return $ mempty { keepalivesCount = return x }- "sslmode" -> return $ mempty { sslmode = return v }- "requiressl" -> do- x <- parseInt "requiressl" v- return $ mempty { requiressl = return x }- "sslcompression" -> do- x <- parseInt "sslcompression" v- return $ mempty { sslcompression = return x }- "sslcert" -> return $ mempty { sslcert = return v }- "sslkey" -> return $ mempty { sslkey = return v }- "sslrootcert" -> return $ mempty { sslrootcert = return v }- "requirepeer" -> return $ mempty { requirepeer = return v }- "krbsrvname" -> return $ mempty { krbsrvname = return v }- "gsslib" -> return $ mempty { gsslib = return v }- "service" -> return $ mempty { service = return v }-- x -> Left $ "Unrecongnized option: " ++ show x--queryToPartialOptions :: URI.Query -> Either String PartialOptions-queryToPartialOptions Query {..} = foldM (\acc (k, v) -> fmap (mappend acc) $ keywordToPartialOptions (BSC.unpack k) $ BSC.unpack v) mempty queryPairs--uriToOptions :: URIRef Absolute -> Either String PartialOptions-uriToOptions URI {..} = case schemeBS uriScheme of- "postgresql" -> do- queryParts <- queryToPartialOptions uriQuery- return $ maybe mempty autorityToPartialOptions uriAuthority <>- pathToPartialOptions uriPath <> queryParts-- x -> Left $ "Wrong protocol. Expected \"postgresql\" but got: " ++ show x--parseURIStr :: String -> Either String (URIRef Absolute)-parseURIStr = left show . parseURI strictURIParserOptions . BSC.pack where- left f = \case- Left x -> Left $ f x- Right x -> Right x--parseKeywords :: String -> Either String PartialOptions-parseKeywords [] = Left "Failed to parse keywords"-parseKeywords x = fmap mconcat . mapM (uncurry keywordToPartialOptions <=< toTuple . splitOn "=") $ words x where- toTuple [k, v] = return (k, v)- toTuple xs = Left $ "invalid opts:" ++ show (intercalate "=" xs)--parseConnectionString :: String -> Either String PartialOptions-parseConnectionString url = do- url' <- maybe (Left "failed to parse as string") Right $ parseString url- parseKeywords url' <|> (uriToOptions =<< parseURIStr url')--toArgs :: Options -> [String]-toArgs Options {..} =- [ "--dbname=" <> oDbname- ]- ++ (("--host=" <>) <$> maybeToList oHost)- ++ (("--username=" <>) <$> maybeToList oUser)- ++ (("--password=" <>) <$> maybeToList oPassword)- ++ ((\x -> "--host=" <> show x) <$> maybeToList oPort)-
+ src/Database/PostgreSQL/Simple/PartialOptions.hs view
@@ -0,0 +1,230 @@+{-| A resuable optparse-applicative parser for creating a postgresql-simple+ 'Connection'+-}+{-# LANGUAGE RecordWildCards, LambdaCase, DeriveGeneric, DeriveDataTypeable #-}+{-# LANGUAGE CPP, GADTs, OverloadedStrings #-}++module Database.PostgreSQL.Simple.PartialOptions where++import Control.Monad ((<=<), foldM)+import Data.ByteString (ByteString)+import qualified Data.ByteString as BS+import qualified Data.ByteString.Char8 as BSC+import Data.Char (isUpper, toLower)+import Data.Default (Default(..))+import qualified Data.Either.Validation as DEV+import Data.Either.Validation (Validation(..), validationToEither)+import Data.List (intercalate)+import Data.List.Split (splitOn)+import Data.Typeable (Typeable)+import Database.PostgreSQL.Simple (ConnectInfo(..), Connection, connectPostgreSQL, defaultConnectInfo)+import Database.PostgreSQL.Simple.Options (Options(..), toConnectionString)+import GHC.Generics (Generic)+import Generics.Deriving.Monoid (Last(..), gmappenddefault, gmemptydefault)+import Options.Applicative (Parser, (<|>), eitherReader, long, option)+import Options.Generic (Modifiers(..), ParseRecord(..), defaultModifiers, parseRecordWithModifiers)+import Text.Read (readMaybe)+import URI.ByteString as URI++-- | An optional version of 'Options'. This includes an instance of+-- | 'ParseRecord' which provides the optparse-applicative Parser.+data PartialOptions = PartialOptions+ { host :: Last String+ , hostaddr :: Last String+ , port :: Last Int+ , user :: Last String+ , password :: Last String+ , dbname :: Last String+ , connectTimeout :: Last Int+ , clientEncoding :: Last String+ , options :: Last String+ , fallbackApplicationName :: Last String+ , keepalives :: Last Int+ , keepalivesIdle :: Last Int+ , keepalivesCount :: Last Int+ , sslmode :: Last String+ , requiressl :: Last Int+ , sslcompression :: Last Int+ , sslcert :: Last String+ , sslkey :: Last String+ , sslrootcert :: Last String+ , requirepeer :: Last String+ , krbsrvname :: Last String+ , gsslib :: Last String+ , service :: Last String+ } deriving (Show, Eq, Read, Ord, Generic, Typeable)++instance ParseRecord PartialOptions where+ parseRecord = option (eitherReader parseConnectionString) (long "connectString")+ <|> parseRecordWithModifiers defaultModifiers++instance Semigroup PartialOptions where+ (<>) = gmappenddefault++instance Monoid PartialOptions where+ mempty = gmemptydefault+ mappend = (<>)++-- Copied from Options.Generic source code+underscoreModifiers :: Modifiers+underscoreModifiers = Modifiers lispCase lispCase (const Nothing)+ where+ lispCase = dropWhile (== '_') . (>>= lower) . dropWhile (== '_')+ lower c | isUpper c = ['_', toLower c]+ | otherwise = [c]+++unSingleQuote :: String -> Maybe String+unSingleQuote (x : xs@(_ : _))+ | x == '\'' && last xs == '\'' = Just $ init xs+ | otherwise = Nothing+unSingleQuote _ = Nothing++parseString :: String -> Maybe String+parseString x = readMaybe x <|> unSingleQuote x <|> Just x++mkLast :: a -> Last a+mkLast = Last . Just++-- | The 'PartialOptions' version of 'defaultOptions'+instance Default PartialOptions where+ def = mempty+ { host = mkLast $ connectHost defaultConnectInfo+ , port = mkLast $ fromIntegral $ connectPort defaultConnectInfo+ , user = mkLast $ connectUser defaultConnectInfo+ , password = mkLast $ connectPassword defaultConnectInfo+ , dbname = mkLast $ connectDatabase defaultConnectInfo+ }++getOption :: String -> Last a -> Validation [String] a+getOption optionName = \case+ Last (Just x) -> pure x+ Last Nothing -> DEV.Failure ["Missing " ++ optionName ++ " option"]++getLast' :: Applicative f => Last a -> f (Maybe a)+getLast' = pure . getLast++completeOptions :: PartialOptions -> Either [String] Options+completeOptions PartialOptions {..} = validationToEither $+ Options <$> getLast' host+ <*> getLast' hostaddr+ <*> (fmap fromIntegral <$> getLast' port)+ <*> getLast' user+ <*> getLast' password+ <*> getOption "dbname" dbname+ <*> getLast' connectTimeout+ <*> getLast' clientEncoding+ <*> getLast' options+ <*> getLast' fallbackApplicationName+ <*> getLast' keepalives+ <*> getLast' keepalivesIdle+ <*> getLast' keepalivesCount+ <*> getLast' sslmode+ <*> getLast' requiressl+ <*> getLast' sslcompression+ <*> getLast' sslcert+ <*> getLast' sslkey+ <*> getLast' sslrootcert+ <*> getLast' requirepeer+ <*> getLast' krbsrvname+ <*> getLast' gsslib+ <*> getLast' service++-- | Useful for testing or if only Options are needed.+completeParser :: Parser Options+completeParser =+ fmap (either (error . unlines) id . completeOptions . mappend def) parseRecord++-- | Create a connection with an 'Option'+run :: Options -> IO Connection+run = connectPostgreSQL . toConnectionString++userInfoToPartialOptions :: UserInfo -> PartialOptions+userInfoToPartialOptions UserInfo {..} = mempty { user = return $ BSC.unpack uiUsername } <> if BS.null uiPassword+ then mempty+ else mempty { password = return $ BSC.unpack uiPassword }++autorityToPartialOptions :: Authority -> PartialOptions+autorityToPartialOptions Authority {..} = maybe mempty userInfoToPartialOptions authorityUserInfo <>+ mempty { host = return $ BSC.unpack $ hostBS authorityHost } <>+ maybe mempty (\p -> mempty { port = return $ portNumber p }) authorityPort++pathToPartialOptions :: ByteString -> PartialOptions+pathToPartialOptions path = case drop 1 $ BSC.unpack path of+ "" -> mempty+ x -> mempty {dbname = return x }++parseInt :: String -> String -> Either String Int+parseInt msg v = maybe (Left (msg <> " value of: " <> v <> " is not a number")) Right $+ readMaybe v++keywordToPartialOptions :: String -> String -> Either String PartialOptions+keywordToPartialOptions k v = case k of+ "host" -> return $ mempty { host = return v }+ "hostaddress" -> return $ mempty { hostaddr = return v }+ "port" -> do+ portValue <- parseInt "port" v+ return $ mempty { port = return portValue }+ "user" -> return $ mempty { user = return v }+ "password" -> return $ mempty { password = return v }+ "dbname" -> return $ mempty { dbname = return v}+ "connect_timeout" -> do+ x <- parseInt "connect_timeout" v+ return $ mempty { connectTimeout = return x }+ "client_encoding" -> return $ mempty { clientEncoding = return v }+ "options" -> return $ mempty { options = return v }+ "fallback_applicationName" -> return $ mempty { fallbackApplicationName = return v }+ "keepalives" -> do+ x <- parseInt "keepalives" v+ return $ mempty { keepalives = return x }+ "keepalives_idle" -> do+ x <- parseInt "keepalives_idle" v+ return $ mempty { keepalivesIdle = return x }+ "keepalives_count" -> do+ x <- parseInt "keepalives_count" v+ return $ mempty { keepalivesCount = return x }+ "sslmode" -> return $ mempty { sslmode = return v }+ "requiressl" -> do+ x <- parseInt "requiressl" v+ return $ mempty { requiressl = return x }+ "sslcompression" -> do+ x <- parseInt "sslcompression" v+ return $ mempty { sslcompression = return x }+ "sslcert" -> return $ mempty { sslcert = return v }+ "sslkey" -> return $ mempty { sslkey = return v }+ "sslrootcert" -> return $ mempty { sslrootcert = return v }+ "requirepeer" -> return $ mempty { requirepeer = return v }+ "krbsrvname" -> return $ mempty { krbsrvname = return v }+ "gsslib" -> return $ mempty { gsslib = return v }+ "service" -> return $ mempty { service = return v }++ x -> Left $ "Unrecongnized option: " ++ show x++queryToPartialOptions :: URI.Query -> Either String PartialOptions+queryToPartialOptions Query {..} = foldM (\acc (k, v) -> fmap (mappend acc) $ keywordToPartialOptions (BSC.unpack k) $ BSC.unpack v) mempty queryPairs++uriToOptions :: URIRef Absolute -> Either String PartialOptions+uriToOptions URI {..} = case schemeBS uriScheme of+ "postgresql" -> do+ queryParts <- queryToPartialOptions uriQuery+ return $ maybe mempty autorityToPartialOptions uriAuthority <>+ pathToPartialOptions uriPath <> queryParts++ x -> Left $ "Wrong protocol. Expected \"postgresql\" but got: " ++ show x++parseURIStr :: String -> Either String (URIRef Absolute)+parseURIStr = left show . parseURI strictURIParserOptions . BSC.pack where+ left f = \case+ Left x -> Left $ f x+ Right x -> Right x++parseKeywords :: String -> Either String PartialOptions+parseKeywords [] = Left "Failed to parse keywords"+parseKeywords x = fmap mconcat . mapM (uncurry keywordToPartialOptions <=< toTuple . splitOn "=") $ words x where+ toTuple [k, v] = return (k, v)+ toTuple xs = Left $ "invalid opts:" ++ show (intercalate "=" xs)++parseConnectionString :: String -> Either String PartialOptions+parseConnectionString url = do+ url' <- maybe (Left "failed to parse as string") Right $ parseString url+ parseKeywords url' <|> (uriToOptions =<< parseURIStr url')
test/Spec.hs view
@@ -1,6 +1,6 @@ {-# LANGUAGE OverloadedStrings, CPP #-} import Test.Hspec-import Database.PostgreSQL.Simple.Options+import Database.PostgreSQL.Simple.PartialOptions import System.Environment import Options.Applicative import System.Exit