packages feed

postgresql-simple-opts 0.2.0.2 → 0.3.0.0

raw patch · 3 files changed

+353/−146 lines, 3 filesdep +generic-derivingdep +splitdep +uri-bytestringPVP ok

version bump matches the API change (PVP)

Dependencies added: generic-deriving, split, uri-bytestring

API changes (from Hackage documentation)

- Database.PostgreSQL.Simple.Options: ConnectString :: ByteString -> ConnectString
- Database.PostgreSQL.Simple.Options: OConnectInfo :: ConnectInfo -> Options
- Database.PostgreSQL.Simple.Options: OConnectString :: ByteString -> Options
- Database.PostgreSQL.Simple.Options: POConnectString :: ConnectString -> PartialOptions
- Database.PostgreSQL.Simple.Options: POPartialConnectInfo :: PartialConnectInfo -> PartialOptions
- Database.PostgreSQL.Simple.Options: PartialConnectInfo :: Last String -> Last Int -> Last String -> Last String -> Last String -> PartialConnectInfo
- Database.PostgreSQL.Simple.Options: [connectString] :: ConnectString -> ByteString
- Database.PostgreSQL.Simple.Options: [database] :: PartialConnectInfo -> Last String
- Database.PostgreSQL.Simple.Options: completeConnectInfo :: PartialConnectInfo -> Either [String] ConnectInfo
- Database.PostgreSQL.Simple.Options: data PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance Data.Default.Class.Default Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance Data.String.IsString Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Base.Monoid Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Eq Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Eq Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Ord Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Classes.Ord Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance GHC.Generics.Generic Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Generics.Generic Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance GHC.Read.Read Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Read.Read Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance GHC.Show.Show Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance GHC.Show.Show Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: instance Options.Generic.ParseRecord Database.PostgreSQL.Simple.Options.ConnectString
- Database.PostgreSQL.Simple.Options: instance Options.Generic.ParseRecord Database.PostgreSQL.Simple.Options.PartialConnectInfo
- Database.PostgreSQL.Simple.Options: newtype ConnectString
- Database.PostgreSQL.Simple.Options: parser :: Parser PartialOptions
+ 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: [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: [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: autorityToPartialOptions :: Authority -> PartialOptions
+ Database.PostgreSQL.Simple.Options: getLast' :: Applicative f => Last a -> f (Maybe a)
+ 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: 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: 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: 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: toArgs :: Options -> [String]
+ Database.PostgreSQL.Simple.Options: toConnectionString :: Options -> ByteString
+ 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.Options: [host] :: PartialConnectInfo -> Last String
+ Database.PostgreSQL.Simple.Options: [host] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [password] :: PartialConnectInfo -> Last String
+ Database.PostgreSQL.Simple.Options: [password] :: PartialOptions -> Last String
- Database.PostgreSQL.Simple.Options: [port] :: PartialConnectInfo -> Last Int
+ Database.PostgreSQL.Simple.Options: [port] :: PartialOptions -> Last Int
- Database.PostgreSQL.Simple.Options: [user] :: PartialConnectInfo -> Last String
+ Database.PostgreSQL.Simple.Options: [user] :: PartialOptions -> Last String

Files

postgresql-simple-opts.cabal view
@@ -1,7 +1,7 @@ name:                postgresql-simple-opts-version:             0.2.0.2+version:             0.3.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 ConnectInfo and connection string.+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 license:             BSD3 license-file:        LICENSE@@ -23,6 +23,9 @@                , either                , optparse-generic >= 1.0.1 && <1.3                , data-default+               , split+               , uri-bytestring+               , generic-deriving   default-language:    Haskell2010   ghc-options: -Wall                -fno-warn-unused-do-bind@@ -37,6 +40,7 @@                      , postgresql-simple                      , optparse-applicative                      , bytestring+                     , data-default   ghc-options: -Wall                -fno-warn-unused-do-bind                -threaded
src/Database/PostgreSQL/Simple/Options.hs view
@@ -2,49 +2,99 @@    'Connection' -} {-# LANGUAGE RecordWildCards, LambdaCase, DeriveGeneric, DeriveDataTypeable #-}-{-# LANGUAGE GeneralizedNewtypeDeriving, CPP #-}+{-# 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.String import Data.Monoid 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 --- | An optional version of 'ConnectInfo'. This includes an instance of+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 PartialConnectInfo = PartialConnectInfo-  { host     :: Last String-  , port     :: Last Int-  , user     :: Last String-  , password :: Last String-  , database :: Last String+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 PartialConnectInfo+instance ParseRecord PartialOptions where+  parseRecord = (option (eitherReader parseConnectionString) (long "connectString"))+    <|> parseRecordWithModifiers defaultModifiers -instance Monoid PartialConnectInfo where-  mempty = PartialConnectInfo (Last Nothing) (Last Nothing)-                              (Last Nothing) (Last Nothing)-                              (Last Nothing)-  mappend x y = PartialConnectInfo-    { host     = host     x <> host     y-    , port     = port     x <> port     y-    , user     = user     x <> user     y-    , password = password x <> password y-    , database = database x <> database y-    }+instance Monoid PartialOptions where+  mempty = gmemptydefault+  mappend = gmappenddefault -newtype ConnectString = ConnectString-  { connectString :: ByteString-  } deriving ( Show, Eq, Read, Ord, Generic, Typeable, IsString )+-- 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@@ -54,78 +104,88 @@ parseString :: String -> Maybe String parseString x = readMaybe x <|> unSingleQuote x <|> Just x -instance ParseRecord ConnectString where-  parseRecord =  fmap (ConnectString . BSC.pack)-              $  option ( eitherReader-                        $ maybe (Left "Impossible!") Right-                        . parseString-                        )-                        (long "connectString")--data PartialOptions-  = POConnectString      ConnectString-  | POPartialConnectInfo PartialConnectInfo-  deriving (Show, Eq, Read, Generic, Typeable)--instance Monoid PartialOptions where-    mempty = POPartialConnectInfo mempty-    mappend a b = case (a, b) of-        (POConnectString x, _) -> POConnectString x-        (POPartialConnectInfo x, POPartialConnectInfo y) ->-            POPartialConnectInfo $ x <> y-        (POPartialConnectInfo _, POConnectString x) -> POConnectString x--instance ParseRecord PartialOptions where-  parseRecord-    =  fmap POConnectString      parseRecord-   <|> fmap POPartialConnectInfo parseRecord---- | The main parser to reuse.-parser :: Parser PartialOptions-parser = parseRecord--data Options-  = OConnectString ByteString-  | OConnectInfo   ConnectInfo-  deriving (Show, Eq, Read, Generic, Typeable)- mkLast :: a -> Last a mkLast = Last . Just --- | The 'PartialConnectInfo' version of 'defaultConnectInfo'-instance Default PartialConnectInfo where-    def = PartialConnectInfo+-- | 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-      , database = mkLast $                connectDatabase defaultConnectInfo+      , dbname = mkLast $                  connectDatabase defaultConnectInfo       } -instance Default PartialOptions where-    def = POPartialConnectInfo def- getOption :: String -> Last a -> Validation [String] a getOption optionName = \case     Last (Just x) -> pure x     Last Nothing  -> Data.Either.Validation.Failure         ["Missing " ++ optionName ++ " option"] -completeConnectInfo :: PartialConnectInfo -> Either [String] ConnectInfo-completeConnectInfo PartialConnectInfo {..} = validationToEither $ do-  ConnectInfo <$> getOption "host"     host-              <*> (fromIntegral <$> getOption "port" port)-              <*> getOption "user"     user-              <*> getOption "password" password-              <*> getOption "database" database+getLast' :: Applicative f => Last a -> f (Maybe a)+getLast' = pure . getLast --- | mappend with 'defaultPartialConnectInfo' if necessary to create all---   options completeOptions :: PartialOptions -> Either [String] Options-completeOptions = \case-  POConnectString   (ConnectString x) -> Right $ OConnectString x-  POPartialConnectInfo x              -> OConnectInfo <$> completeConnectInfo x+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 =@@ -133,6 +193,104 @@  -- | Create a connection with an 'Option' run :: Options -> IO Connection-run = \case-  OConnectString connString -> connectPostgreSQL connString-  OConnectInfo   connInfo   -> connect           connInfo+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)+
test/Spec.hs view
@@ -1,10 +1,10 @@ {-# LANGUAGE OverloadedStrings, CPP #-} import Test.Hspec import Database.PostgreSQL.Simple.Options-import Database.PostgreSQL.Simple import System.Environment import Options.Applicative import System.Exit+import Data.Default #if !MIN_VERSION_base(4,8,0) import Data.Monoid #endif@@ -13,76 +13,121 @@ testParser = execParser $ info completeParser mempty  main :: IO ()-main = hspec $ describe "Options Parser" $ do-  it "parses all options" $ do-    let testArgs = [ "--host=example.com"-                   , "--port=1234"-                   , "--user=nobody"-                   , "--password=everytimeiclosemyeyes"-                   , "--database=future"-                   ]+main = hspec $ do+  describe "Options Parser" $ do+    it "parses all options" $ do+      let testArgs = [ "--host=example.com"+                     , "--port=1234"+                     , "--user=nobody"+                     , "--password=everytimeiclosemyeyes"+                     , "--dbname=future"+                     ] -        expected = OConnectInfo $ ConnectInfo-                   { connectHost     = "example.com"-                   , connectPort     = 1234-                   , connectUser     = "nobody"-                   , connectPassword = "everytimeiclosemyeyes"-                   , connectDatabase = "future"-                   }+          expected = completeOptions $ mempty+                     { host     = return "example.com"+                     , port     = return 1234+                     , user     = return "nobody"+                     , password = return "everytimeiclosemyeyes"+                     , dbname   = return "future"+                     } -    actual <- withArgs testArgs testParser-    actual `shouldBe` expected+      actual <- withArgs testArgs testParser+      Right actual `shouldBe` expected -  it "parses some and uses defaults for others" $ do-    let testArgs = [ "--user=nobody"-                   , "--password=everytimeiclosemyeyes"-                   , "--database=future"-                   ]+    it "parses some and uses defaults for others" $ do+      let testArgs = [ "--user=nobody"+                     , "--password=everytimeiclosemyeyes"+                     , "--dbname=future"+                     ] -        expected = OConnectInfo $ ConnectInfo-                   { connectHost     = "127.0.0.1"-                   , connectPort     = 5432-                   , connectUser     = "nobody"-                   , connectPassword = "everytimeiclosemyeyes"-                   , connectDatabase = "future"-                   }+          expected = completeOptions $ mempty+                     { host     = return "127.0.0.1"+                     , port     = return 5432+                     , user     = return "nobody"+                     , password = return "everytimeiclosemyeyes"+                     , dbname   = return "future"+                     } -    actual <- withArgs testArgs testParser-    actual `shouldBe` expected+      actual <- withArgs testArgs testParser+      Right actual `shouldBe` expected -  it "parses no options and gives defaults" $ do-    let expected = OConnectInfo $ ConnectInfo-                   { connectHost     = "127.0.0.1"-                   , connectPort     = 5432-                   , connectUser     = "postgres"-                   , connectPassword = ""-                   , connectDatabase = ""-                   }+    it "parses no options and gives defaults" $ do+      let expected = completeOptions $ mempty+                     { host     = return "127.0.0.1"+                     , port     = return 5432+                     , user     = return "postgres"+                     , password = return ""+                     , dbname   = return ""+                     } -    actual <- withArgs [] testParser-    actual `shouldBe` expected-  it "parses the connection string double quoted" $ do-    let testArgs = ["--connectString=\"a b\""]-        expected = OConnectString "a b"+      actual <- withArgs [] testParser+      Right actual `shouldBe` expected+    it "parses the connection string double quoted" $ do+      let testArgs = ["--connectString=\"host=yahoo\""]+          expected = completeOptions $ def { host = return "yahoo" } -    actual <- withArgs testArgs testParser-    actual `shouldBe` expected-  it "parses the connection string single quoted" $ do-    let testArgs = ["--connectString='a b'"]-        expected = OConnectString "a b"+      actual <- withArgs testArgs testParser+      Right actual `shouldBe` expected+    it "parses the connection string single quoted" $ do+      let testArgs = ["--connectString='host=yahoo'"]+          expected = completeOptions $ def { host = return "yahoo" } -    actual <- withArgs testArgs testParser-    actual `shouldBe` expected-  it "parses the connection string no quotes" $ do-    let testArgs = ["--connectString=a_b"]-        expected = OConnectString "a_b"+      actual <- withArgs testArgs testParser+      Right actual `shouldBe` expected+    it "parses the connection string no quotes" $ do+      let testArgs = ["--connectString=host=yahoo"]+          expected = completeOptions $ def { host = return "yahoo" } -    actual <- withArgs testArgs testParser-    actual `shouldBe` expected-  it "fails if connectString and other args are passed" $ do-    let testArgs = ["--connectString=a_b", "--port=1234"]-        handler :: ExitCode -> Bool-        handler (ExitFailure 1) = True-        handler _ = False+      actual <- withArgs testArgs testParser+      Right actual `shouldBe` expected+    it "fails if connectString and other args are passed" $ do+      let testArgs = ["--connectString=host=yahoo", "--port=1234"]+          handler :: ExitCode -> Bool+          handler (ExitFailure 1) = True+          handler _ = False -    shouldThrow (withArgs testArgs testParser) handler+      shouldThrow (withArgs testArgs testParser) handler++  describe "connection string parser" $ do+    it "fails on empty" $ parseConnectionString "" `shouldBe` Left "MalformedScheme NonAlphaLeading"++    it "parses a single keyword" $ parseConnectionString "host=localhost"+      `shouldBe` Right (mempty { host = return "localhost" })++    it "parses all keywords" $ parseConnectionString+      "host=localhost port=1234 user=jonathan password=open dbname=dev" `shouldBe`+        Right (mempty+          { host = return "localhost"+          , port = return 1234+          , user = return "jonathan"+          , password = return "open"+          , dbname = return "dev"+          })++    it "parses host only connection string" $ parseConnectionString "postgresql://localhost"+      `shouldBe` Right (mempty { host = return "localhost" })++    it "parses all params" $ parseConnectionString "postgresql://jonathan:open@localhost:1234/dev"+      `shouldBe` Right (mempty+          { host = return "localhost"+          , port = return 1234+          , user = return "jonathan"+          , password = return "open"+          , dbname = return "dev"+          })++    it "parses all params using query params" $ parseConnectionString "postgresql:///dev?host=localhost&port=1234"+      `shouldBe` Right (mempty+        { host = return "localhost"+        , port = return 1234+        , dbname = return "dev"+        })++    it "decodes a unix port" $ parseConnectionString "postgresql://%2Fvar%2Flib%2Fpostgresql/dbname"+      `shouldBe` Right (mempty+          { host = return "/var/lib/postgresql"+          , dbname = return "dbname"+          })+++