packages feed

postgresql-simple-opts 0.1.0.5 → 0.2.0.0

raw patch · 2 files changed

+45/−24 lines, 2 filesdep +data-defaultdep +either

Dependencies added: data-default, either

Files

postgresql-simple-opts.cabal view
@@ -1,5 +1,5 @@ name:                postgresql-simple-opts-version:             0.1.0.5+version:             0.2.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. homepage:            https://github.com/jfischoff/postgresql-simple-opts#readme@@ -20,8 +20,10 @@                , postgresql-simple                , optparse-applicative >=0.11.0 && <0.14                , bytestring+               , either                , optparse-generic >= 1.0.1 && <1.2                , markdown-unlit >= 0.4.0 && <0.5+               , data-default   default-language:    Haskell2010   ghc-options: -pgmL markdown-unlit                -Wall
src/Database/PostgreSQL/Simple/Options.lhs view
@@ -23,7 +23,7 @@    'Connection' -} {-# LANGUAGE RecordWildCards, LambdaCase, DeriveGeneric, DeriveDataTypeable #-}-{-# LANGUAGE GeneralizedNewtypeDeriving, CPP #-}+{-# LANGUAGE GeneralizedNewtypeDeriving, CPP, ApplicativeDo #-} module Database.PostgreSQL.Simple.Options where import Database.PostgreSQL.Simple import Options.Applicative@@ -35,6 +35,8 @@ import Data.Typeable import Data.String import Data.Monoid+import Data.Either.Validation+import Data.Default ```  ### <a name="partial"> The "Partial" Option Types@@ -116,6 +118,14 @@   = 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 ```  There is one wrinkle. `optparse-generic` treats sum types as "commands". This makes sense as a default, but it is not what we want. We want to choose one record or another based on the non-overlapping flags. This is easy enough to do by hand.@@ -157,29 +167,37 @@ mkLast = Last . Just  -- | The 'PartialConnectInfo' version of 'defaultConnectInfo'-defaultPartialConnectInfo :: PartialConnectInfo-defaultPartialConnectInfo = PartialConnectInfo-  { host     = mkLast $                connectHost     defaultConnectInfo-  , port     = mkLast $ fromIntegral $ connectPort     defaultConnectInfo-  , user     = mkLast $                connectUser     defaultConnectInfo-  , password = mkLast $                connectPassword defaultConnectInfo-  , database = mkLast $                connectDatabase defaultConnectInfo-  }+instance Default PartialConnectInfo where+    def = PartialConnectInfo+      { host     = mkLast $                connectHost     defaultConnectInfo+      , port     = mkLast $ fromIntegral $ connectPort     defaultConnectInfo+      , user     = mkLast $                connectUser     defaultConnectInfo+      , password = mkLast $                connectPassword defaultConnectInfo+      , database = mkLast $                connectDatabase defaultConnectInfo+      }++instance Default PartialOptions where+    def = POPartialConnectInfo def ```  We can now complete the `PartialConnectInfo` to get a `ConnectInfo`.  ```haskell-completeConnectInfo :: PartialConnectInfo -> ConnectInfo-completeConnectInfo x = case defaultPartialConnectInfo <> x of-  PartialConnectInfo-    { host     = Last (Just connectHost    )-    , port     = Last (Just connectPortInt )-    , user     = Last (Just connectUser    )-    , password = Last (Just connectPassword)-    , database = Last (Just connectDatabase)-    } -> let connectPort = fromIntegral connectPortInt in ConnectInfo {..}-  _ -> error "Impossible! No options should be required!"+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+  connectHost     <- getOption "host"     host+  connectPort     <- fromIntegral+                 <$> getOption "port"     port+  connectUser     <- getOption "user"     user+  connectPassword <- getOption "password" password+  connectDatabase <- getOption "database" database+  return $ ConnectInfo {..} ```  Completing a `PartialOptions` to get an `Options` follows straightforwardly ... if you've done this a bunch I suppose.@@ -187,10 +205,10 @@ ```haskell -- | mappend with 'defaultPartialConnectInfo' if necessary to create all --   options-completeOptions :: PartialOptions -> Options+completeOptions :: PartialOptions -> Either [String] Options completeOptions = \case-  POConnectString   (ConnectString x) -> OConnectString x-  POPartialConnectInfo x              -> OConnectInfo $ completeConnectInfo x+  POConnectString   (ConnectString x) -> Right $ OConnectString x+  POPartialConnectInfo x              -> OConnectInfo <$> completeConnectInfo x ```  ### <a name="option-parser"> The Option Parser@@ -200,7 +218,8 @@ ```haskell -- | Useful for testing or if only Options are needed. completeParser :: Parser Options-completeParser = fmap completeOptions parseRecord+completeParser =+    fmap (either (error . unlines) id . completeOptions . mappend def) parseRecord ```  ### <a name="runner"> The Runner