packages feed

getopt-generics 0.9 → 0.10

raw patch · 10 files changed

+278/−297 lines, 10 filesdep +directorydep +filepathdep +processdep −hspec-expectationsdep −markdown-unlitPVP ok

version bump matches the API change (PVP)

Dependencies added: directory, filepath, process, temporary

Dependencies removed: hspec-expectations, markdown-unlit

API changes (from Hackage documentation)

- System.Console.GetOpt.Generics: _accumulate :: Option a => a -> a -> a
- System.Console.GetOpt.Generics: _emptyOption :: Option a => Modifiers -> FieldString -> FieldState a
- System.Console.GetOpt.Generics: _toOption :: Option a => ArgDescr (FieldState a)

Files

− README.md
@@ -1,104 +0,0 @@-# getopt-generics--## Status--This library is experimental.--## Usage--`getopt-generics` tries to make it very simple to create executables that parse-command line options. All you have to do is to define a type and derive some-instances:--~~~ {.haskell}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Readme where--import Data.Typeable-import GHC.Generics-import System.Console.GetOpt.Generics-import System.Environment--data Options-  = Options {-    port :: Int,-    daemonize :: Bool,-    config :: Maybe FilePath-  }-  deriving (Show, GHC.Generics.Generic)--instance System.Console.GetOpt.Generics.Generic Options-instance HasDatatypeInfo Options-~~~--Then you can use `getArguments` to create a command-line argument parser:--~~~ {.haskell}-main :: IO ()-main = do-  options <- getArguments-  print (options :: Options)-~~~--This program has--- a non-optional `--port` flag with an integer argument,-- a boolean flag `--daemonize`,-- an optional flag `--config` expecting a file argument and-- a generic `--help` option.--Here's in example of the program above in bash:-``` bash-$ program --port 8080 --config some/path-Options {port = 8080, daemonize = False, config = Just "some/path"}-$ program  --port 8080 --daemonize-Options {port = 8080, daemonize = True, config = Nothing}-$ program --port foo-not an integer: foo-$ program-missing option: --port=int-$ program --help-program-    --port=integer-    --daemonize-    --config=string (optional)-```--## Constraints--There are some constraints that the defined datatype has to fulfill:--  * It has to have only one constructor,-  * that constructor has to have field selectors (i.e. use record syntax) and-  * all fields have to be of a type that has an instance for `Option`.--(Types declared with `newtype` are allowed with the same constraints.)--## Using Custom Field Types--It is possible to use custom field types by providing an instance for `Option`.-Here's an example:--~~~ {.haskell}-data File = File FilePath-  deriving (Show, Typeable)--instance Option File where-  argumentType Proxy = "file"-  parseArgument f = Just (File f)--data FileOptions-  = FileOptions {-    file :: File-  }-  deriving (Show, GHC.Generics.Generic)--instance System.Console.GetOpt.Generics.Generic FileOptions-instance HasDatatypeInfo FileOptions---- Returns: FileOptions {file = File "some/file"}-getFileOptions :: IO FileOptions-getFileOptions = withArgs (words "--file some/file") getArguments-~~~
− examples/Example.hs
@@ -1,28 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}--module Example where--import qualified GHC.Generics-import           System.Console.GetOpt.Generics---- we specify a data type that maps to the commandline arguments--data Foo-  = Foo {-      bar :: String-    , baz :: Maybe String-    , qux :: Int-    }-    deriving (Eq, Show, GHC.Generics.Generic)--instance Generic Foo-instance HasDatatypeInfo Foo---- give it go--main :: IO ()-main = do-  myFoo <- getArguments-  print $ bar myFoo-  print $ baz myFoo-  print $ qux myFoo
− examples/Readme.lhs
@@ -1,104 +0,0 @@-# getopt-generics--## Status--This library is experimental.--## Usage--`getopt-generics` tries to make it very simple to create executables that parse-command line options. All you have to do is to define a type and derive some-instances:--~~~ {.haskell}-{-# LANGUAGE DeriveDataTypeable #-}-{-# LANGUAGE DeriveGeneric #-}--module Readme where--import Data.Typeable-import GHC.Generics-import System.Console.GetOpt.Generics-import System.Environment--data Options-  = Options {-    port :: Int,-    daemonize :: Bool,-    config :: Maybe FilePath-  }-  deriving (Show, GHC.Generics.Generic)--instance System.Console.GetOpt.Generics.Generic Options-instance HasDatatypeInfo Options-~~~--Then you can use `getArguments` to create a command-line argument parser:--~~~ {.haskell}-main :: IO ()-main = do-  options <- getArguments-  print (options :: Options)-~~~--This program has--- a non-optional `--port` flag with an integer argument,-- a boolean flag `--daemonize`,-- an optional flag `--config` expecting a file argument and-- a generic `--help` option.--Here's in example of the program above in bash:-``` bash-$ program --port 8080 --config some/path-Options {port = 8080, daemonize = False, config = Just "some/path"}-$ program  --port 8080 --daemonize-Options {port = 8080, daemonize = True, config = Nothing}-$ program --port foo-not an integer: foo-$ program-missing option: --port=int-$ program --help-program-    --port=integer-    --daemonize-    --config=string (optional)-```--## Constraints--There are some constraints that the defined datatype has to fulfill:--  * It has to have only one constructor,-  * that constructor has to have field selectors (i.e. use record syntax) and-  * all fields have to be of a type that has an instance for `Option`.--(Types declared with `newtype` are allowed with the same constraints.)--## Using Custom Field Types--It is possible to use custom field types by providing an instance for `Option`.-Here's an example:--~~~ {.haskell}-data File = File FilePath-  deriving (Show, Typeable)--instance Option File where-  argumentType Proxy = "file"-  parseArgument f = Just (File f)--data FileOptions-  = FileOptions {-    file :: File-  }-  deriving (Show, GHC.Generics.Generic)--instance System.Console.GetOpt.Generics.Generic FileOptions-instance HasDatatypeInfo FileOptions---- Returns: FileOptions {file = File "some/file"}-getFileOptions :: IO FileOptions-getFileOptions = withArgs (words "--file some/file") getArguments-~~~
getopt-generics.cabal view
@@ -3,9 +3,9 @@ -- see: https://github.com/sol/hpack  name:           getopt-generics-version:        0.9-synopsis:       Simple command line argument parsing-description:    "getopt-generics" tries to make it very simple to create command line argument parsers. An introductory example can be found in the <https://github.com/zalora/getopt-generics#getopt-generics README>.+version:        0.10+synopsis:       Create command line interfaces with ease+description:    Create command line interfaces with ease category:       Console, System homepage:       https://github.com/zalora/getopt-generics#readme bug-reports:    https://github.com/zalora/getopt-generics/issues@@ -17,9 +17,6 @@ build-type:     Simple cabal-version:  >= 1.10 -extra-source-files:-    README.md- source-repository head   type: git   location: https://github.com/zalora/getopt-generics@@ -44,14 +41,45 @@       System.Console.GetOpt.Generics.Simple   default-language: Haskell2010 +test-suite examples+  type: exitcode-stdio-1.0+  main-is: Examples.hs+  hs-source-dirs:+      test+  ghc-options: -Wall -fno-warn-name-shadowing -threaded -O0+  build-depends:+      base == 4.*+    , base-compat >= 0.8+    , base-orphans+    , generics-sop+    , tagged+    , hspec >= 2.1.8+    , temporary+    , directory+    , filepath+    , process+    , silently+  other-modules:+      BashProtocol+      ModifiersSpec+      ModifiersSpec.RenameOptionsSpec+      ModifiersSpec.UseForPositionalArgumentsSpec+      Spec+      System.Console.GetOpt.Generics.FieldStringSpec+      System.Console.GetOpt.Generics.ModifierSpec+      System.Console.GetOpt.Generics.ResultSpec+      System.Console.GetOpt.Generics.SimpleSpec+      System.Console.GetOpt.GenericsSpec+      Util+  default-language: Haskell2010+ test-suite spec   type: exitcode-stdio-1.0   main-is: Spec.hs   hs-source-dirs:       src     , test-    , examples-  ghc-options: -Wall -fno-warn-name-shadowing -threaded -O0 -pgmL markdown-unlit+  ghc-options: -Wall -fno-warn-name-shadowing -threaded -O0   build-depends:       base == 4.*     , base-compat >= 0.8@@ -59,8 +87,6 @@     , generics-sop     , tagged     , hspec >= 2.1.8-    , hspec-expectations-    , markdown-unlit     , QuickCheck     , silently   other-modules:@@ -70,7 +96,8 @@       System.Console.GetOpt.Generics.Modifier       System.Console.GetOpt.Generics.Result       System.Console.GetOpt.Generics.Simple-      ExamplesSpec+      BashProtocol+      Examples       ModifiersSpec       ModifiersSpec.RenameOptionsSpec       ModifiersSpec.UseForPositionalArgumentsSpec@@ -80,6 +107,4 @@       System.Console.GetOpt.Generics.SimpleSpec       System.Console.GetOpt.GenericsSpec       Util-      Example-      Readme   default-language: Haskell2010
src/System/Console/GetOpt/Generics.hs view
@@ -2,6 +2,8 @@ module System.Console.GetOpt.Generics (   -- * IO API   simpleCLI,+  SimpleCLI,+  ArgumentTypes,   getArguments,   modifiedGetArguments,   -- * Pure API@@ -11,11 +13,7 @@   Modifier(..),   deriveShortOptions,   -- * Available Field Types-  -- fixme: hide methods?-  Option(..),-  -- * SimpleCLI class-  SimpleCLI,-  ArgumentTypes,+  Option(argumentType, parseArgument),   -- * Re-exports from "Generics.SOP"   Generic,   HasDatatypeInfo,
src/System/Console/GetOpt/Generics/GetArguments.hs view
@@ -20,10 +20,8 @@ {-# OPTIONS_GHC -fno-warn-unrecognised-pragmas #-}  -- | @getopt-generics@ tries to make it very simple to create command line--- argument parsers. An introductory example can be found in the--- <https://github.com/zalora/getopt-generics#getopt-generics README>.--module System.Console.GetOpt.Generics.GetArguments  where+-- argument parsers.+module System.Console.GetOpt.Generics.GetArguments where  import           Data.Orphans () import           Prelude ()@@ -46,13 +44,62 @@ -- | Parses command line arguments (gotten from 'withArgs') and returns the --   parsed value. This function should be enough for simple use-cases. -----   May throw the following exceptions:+--   Throws the same exceptions as 'simpleCLI'. -----   - @'ExitFailure' 1@ in case of invalid options. Error messages are written---     to @stderr@.---   - @'ExitSuccess'@ in case @--help@ is given. (@'ExitSuccess'@ behaves like---     a normal exception, except that -- if uncaught -- the process will exit---     with exit-code @0@.) Help output is written to @stdout@.+-- Here's an example:++-- ### Start "docs/RecordTypeExample.hs" Haddock ###++-- |+-- >  {-# LANGUAGE DeriveGeneric #-}+-- >+-- >  import GHC.Generics+-- >  import System.Console.GetOpt.Generics+-- >+-- >  -- All you have to do is to define a type and derive some instances:+-- >+-- >  data Options+-- >    = Options {+-- >      port :: Int,+-- >      daemonize :: Bool,+-- >      config :: Maybe FilePath+-- >    }+-- >    deriving (Show, GHC.Generics.Generic)+-- >+-- >  instance System.Console.GetOpt.Generics.Generic Options+-- >  instance HasDatatypeInfo Options+-- >+-- >  -- Then you can use `getArguments` to create a command-line argument parser:+-- >+-- >  main :: IO ()+-- >  main = do+-- >    options <- getArguments+-- >    print (options :: Options)++-- ### End ###++-- | And this is how the above program behaves:++-- ### Start "docs/RecordTypeExample.bash-protocol" Haddock ###++-- |+-- >  $ program --port 8080 --config some/path+-- >  Options {port = 8080, daemonize = False, config = Just "some/path"}+-- >  $ program  --port 8080 --daemonize+-- >  Options {port = 8080, daemonize = True, config = Nothing}+-- >  $ program --port foo+-- >  cannot parse as INTEGER: foo+-- >  $ program+-- >  missing option: --port=INTEGER+-- >  $ program --help+-- >  program [OPTIONS]+-- >        --port=INTEGER+-- >        --daemonize+-- >        --config=STRING (optional)+-- >    -h  --help                      show help and exit++-- ### End ###+ getArguments :: forall a . (Generic a, HasDatatypeInfo a, All2 Option (Code a)) =>   IO a getArguments = modifiedGetArguments []@@ -65,7 +112,7 @@   progName <- getProgName   handleResult $ parseArguments progName modifiers args --- | Pure variant of 'getArguments'.+-- | Pure variant of 'modifiedGetArguments'. -- --   Does not throw any exceptions. parseArguments :: forall a . (Generic a, HasDatatypeInfo a, All2 Option (Code a)) =>@@ -316,9 +363,44 @@ -- --   If you want to use custom field types you should implement an --   @instance Option YourCustomType@ containing implementations of---   'argumentType' and 'parseArgument' (the minimal complete definition). For---   an example see the---   <https://github.com/zalora/getopt-generics#getopt-generics README>.+--   'argumentType' and 'parseArgument' (the minimal complete definition).+--+-- Here's an example:++-- ### Start "docs/CustomOptionsExample.hs" Haddock ###++-- |+-- >  {-# LANGUAGE DeriveDataTypeable #-}+-- >+-- >  import           Data.Typeable+-- >  import           System.Console.GetOpt.Generics+-- >+-- >  data File = File FilePath+-- >    deriving (Show, Typeable)+-- >+-- >  instance Option File where+-- >    argumentType Proxy = "custom-file-type"+-- >    parseArgument f = Just (File f)+-- >+-- >  main :: IO ()+-- >  main = simpleCLI $ \ file -> do+-- >    print (file :: File)++-- ### End ###++-- | This would give you:++-- ### Start "docs/CustomOptionsExample.bash-protocol" Haddock ###++-- |+-- >  $ program some/file+-- >  File "some/file"+-- >  $ program --help+-- >  program [OPTIONS] custom-file-type+-- >    -h  --help  show help and exit++-- ### End ###+ class Typeable a => Option a where   {-# MINIMAL argumentType, parseArgument #-}   -- | Name of the argument type, e.g. "bool" or "integer".
src/System/Console/GetOpt/Generics/Simple.hs view
@@ -26,28 +26,49 @@   _initialFieldStates :: Proxy main -> NP FieldState (ArgumentTypes main)   _run :: NP I (ArgumentTypes main) -> main -> IO () --- | 'simpleCLI' converts an IO operation into a program with a nice CLI. @main@ can have---   arbitrarily many parameters provided all parameters have an instance for 'Option'.------   Example:------   Given a file @myProgram.hs@:+-- | 'simpleCLI' converts an IO operation into a program with a proper CLI.+--   Retrieves command line arguments through 'withArgs'.+--   @main@ (the given IO operation) can have arbitrarily many parameters+--   provided all parameters have an instance for 'Option'. -----   > main :: IO ()---   > main = simpleCLI myMain---   >---   > myMain :: String -> Int -> Bool -> IO ()---   > myMain s i b = print (s, i, b)+--   May throw the following exceptions: -----   you get:+--   - @'ExitFailure' 1@ in case of invalid options. Error messages are written+--     to @stderr@.+--   - @'ExitSuccess'@ in case @--help@ is given. (@'ExitSuccess'@ behaves like+--     a normal exception, except that -- if uncaught -- the process will exit+--     with exit-code @0@.) Help output is written to @stdout@. -----   > $ runhaskell myProgram.hs foo 42 true---   > ("foo",42,True)---   > $ runhaskell myProgram.hs foo 42 bar---   > cannot parse as BOOL: bar---   > $ runhaskell myProgram.hs --help---   > myProgram.hs [OPTIONS] STRING INTEGER BOOL---   >   -h  --help  show help and exit+--   Example:++-- ### Start "docs/SimpleExample.hs" Haddock ###++-- |+-- >  import           System.Console.GetOpt.Generics+-- >+-- >  main :: IO ()+-- >  main = simpleCLI myMain+-- >+-- >  myMain :: String -> Int -> Bool -> IO ()+-- >  myMain s i b = print (s, i, b)++-- ### End ###++-- | Using the above program in bash:++-- ### Start "docs/SimpleExample.bash-protocol" Haddock ###++-- |+-- >  $ program foo 42 true+-- >  ("foo",42,True)+-- >  $ program foo 42 bar+-- >  cannot parse as BOOL: bar+-- >  $ program --help+-- >  program [OPTIONS] STRING INTEGER BOOL+-- >    -h  --help  show help and exit++-- ### End ###+ simpleCLI :: forall main . (SimpleCLI main, All Option (ArgumentTypes main)) =>   main -> IO () simpleCLI main = do
+ test/BashProtocol.hs view
@@ -0,0 +1,44 @@++module BashProtocol (testBashProtocol) where++import           Control.Exception+import           Control.Monad+import           Data.List+import           System.Environment.Compat+import           System.FilePath+import           System.IO+import           System.IO.Silently+import           System.Process+import           Test.Hspec++testBashProtocol :: FilePath -> String -> IO ()+testBashProtocol program bashProtocol = do+  assert (takeFileName program == "program") (return ())+  let protocol = parseProtocol bashProtocol+  testProtocol (takeDirectory program) protocol++data Protocol+  = Protocol {+    _command :: String,+    _expected :: [String]+  }+  deriving (Show)++parseProtocol :: String -> [Protocol]+parseProtocol = inner . lines+  where+    inner :: [String] -> [Protocol]+    inner [] = []+    inner (('$' : ' ' : command) : rest) =+      let (expected, next) = span (not . ("$ " `isPrefixOf`)) rest in+      Protocol command expected : inner next+    inner lines = error ("parseProtocol: cannot parse: " ++ show lines)++testProtocol :: FilePath -> [Protocol] -> IO ()+testProtocol programDir protocol = do+  path <- getEnv "PATH"+  setEnv "PATH" (programDir ++ ":" ++ path)+  forM_ protocol $ \ (Protocol command expected) -> do+    hPutStrLn stderr ("running: " ++ command)+    output <- hCapture_ [stdout, stderr] $ system command+    output `shouldBe` unlines expected
+ test/Examples.hs view
@@ -0,0 +1,57 @@++import           Prelude ()+import           Prelude.Compat++import           Control.Monad+import           Data.List+import           System.Directory+import           System.Exit.Compat+import           System.FilePath+import           System.IO.Temp+import           System.Process+import           Test.Hspec++import           BashProtocol++main :: IO ()+main = hspec spec++spec :: Spec+spec = do+  files <- runIO $+    map ("docs" </>) <$>+    filter ((".hs" ==) . takeExtension) <$>+    sort <$>+    getDirectoryContents "docs"++  forM_ files $ \ file -> do+    describe "docs directory ghc" $ do+      it ("compile and run " ++ file) $ do+        withSystemTempDirectory "getopt-generics-test" $ \ tempDir -> do+          compile tempDir file >>= execute file++compile :: FilePath -> FilePath -> IO FilePath+compile tempDir file = do+  let program = tempDir </> "program"+  callCommandCompat $ "cabal exec -- ghc " ++ file +++    " -outputdir " ++ (tempDir </> "build") +++    " -o " ++ program +++    " -isrc -O0" +++    " -Wall -Werror -fno-warn-unused-binds -fno-warn-name-shadowing"+  return program++execute :: FilePath -> FilePath -> IO ()+execute haskellFile program = do+  let bashProtocolFile = dropExtension haskellFile <.> "bash-protocol"+  exists <- doesFileExist bashProtocolFile+  if not exists+    then callCommandCompat $ program ++ " --help"+    else do+      testBashProtocol program =<< readFile bashProtocolFile++callCommandCompat :: String -> IO ()+callCommandCompat command = do+  exitCode <- system command+  case exitCode of+    ExitSuccess -> return ()+    failure@ExitFailure{} -> exitWith failure
− test/ExamplesSpec.hs
@@ -1,10 +0,0 @@--module ExamplesSpec where--import           Test.Hspec--import           Example ()-import           Readme ()--spec :: Spec-spec = return ()