{-# LANGUAGE PatternSynonyms #-}

module Options.Harg
  ( -- * Summary
    -- $summary

    -- ** Option declaration
    option,
    flag,
    switch,
    switch',
    argument,
    Single (..),
    single,
    fromSingle,
    Nested (..),
    getNested,
    nested,
    fromNested,
    AssocListF (..),
    (:+),
    pattern (:+),
    (:->),
    (:*) (..),
    Tagged (..),

    -- ** Option modifiers
    long,
    short,
    help,
    metavar,
    envVar,
    defaultVal,
    defaultStr,
    required,
    optional,
    Opt,

    -- ** Option parsers
    parseWith,
    readParser,
    strParser,
    boolParser,
    manyParser,

    -- ** Executing options
    execOpt,
    execOptDef,
    execOptWithCtx,
    execOptWithCtxDef,
    execCommands,
    execCommandsDef,
    execCommandsWithCtx,
    execCommandsWithCtxDef,

    -- ** Option sources
    EnvSource (..),
    JSONSource (..),
    YAMLSource (..),
    ConfigFile (..),
    noSources,
    defaultSources,

    -- ** Parser context
    getCtx,
    ctxFromArgs,
    ctxFromEnv,
    pureCtx,

    -- ** Variant
    VariantF (..),
    fromVariantF,
    pattern In1,
    pattern In2,
    pattern In3,
    pattern In4,
    pattern In5,

    -- ** Re-exports

    -- *** barbies
    B.FunctorB,
    B.TraversableB,
    B.ApplicativeB,
    B.Rec (..),

    -- *** higgledy
    HKD.HKD,
    HKD.Build,
    HKD.build,
    HKD.Construct,
    HKD.construct,
  )
where

import qualified Barbies as B
import qualified Data.Generic.HKD as HKD
import Options.Harg.Construct
import Options.Harg.Het.HList
import Options.Harg.Het.Prod
import Options.Harg.Het.Variant
import Options.Harg.Nested
import Options.Harg.Operations
import Options.Harg.Single
import Options.Harg.Sources
import Options.Harg.Sources.Env
import Options.Harg.Sources.JSON
import Options.Harg.Sources.NoSource
import Options.Harg.Sources.Types
import Options.Harg.Sources.YAML
import Options.Harg.Types

-- $summary
--
-- @harg@ is a wrapper around @optparse-applicative@ that allows blending
-- command-line configuration with environment variables, defaults as well as
-- other sources such as JSON or YAML files. Here are some very simple examples:
--
-- * Flat configuration type
--
-- @
--   data Config
--     = Config
--         { host :: String
--         , port :: Int
--         , log  :: Bool
--         , dir  :: Maybe String
--         }
--
--   -- Using 'HKD' from higgledy
--   configOpt :: HKD Config Opt
--   configOpt
--     = build @Config hostOpt portOpt logOpt dirOpt
--     where
--       hostOpt
--         = optionWith strParser
--             ( long \"host\"
--             . short \'h\'
--             . help \"Hostname\"
--             . envVar \"HOST_NAME\"
--             )
--       portOpt
--         = optionWith readParser
--             ( long \"port\"
--             . short \'p\'
--             . help \"Port number\"
--             . defaultVal 5432
--             )
--       logOpt
--         = switchWith
--             ( long \"log\"
--             . help \"Whether to log or not\"
--             )
--       dirOpt
--         = argumentWith strParser
--             ( help \"Some directory\"
--             . envVar \"SOME_DIR\"
--             . optional
--             )
--
--   main :: IO Config
--   main = do
--     result <- execOpt defaultSources configOpt
--     pure $ runIdentity (construct result)
-- @
--
-- The above could also be:
--
-- @
--   type ConfigOpt
--     =  Single String
--     :* Single Int
--     :* Single Bool
--     :* Single String
--
--   configOpt :: ConfigOpt Opt
--   configOpt
--     = hostOpt :* portOpt :* logOpt :* dirOpt
--     where
--       ...
--
--   main :: IO Config
--   main = do
--     host :* port :* log :* dir <- execOpt defaultSources configOpt
--     pure
--       $ runIdentity
--       $ Config
--       \<$\> getSingle host
--       \<*\> getSingle port
--       \<*\> getSingle log
--       \<*\> getSingle dir
-- @
--
-- * Nested configuration type
--
-- @
--   data Config
--     = Config
--         { dbConfig :: DbConfig
--         , serverConfig :: ServerConfig
--         }
--
--   data DbConfig
--     = DbConfig
--         { dbHost :: String
--         , dbPort :: Int
--         }
--
--   data ServerConfig
--     = ServerConfig
--         { srvPort :: Int
--         , srvLog  :: Bool
--         }
--
--   type ConfigOpt
--     =  HKD DbConfig
--     :* HKD ServerConfig
--
--   configOpt :: ConfigOpt Opt
--   configOpt
--     = dbOpt :* srvOpt
--     where
--       dbOpt = build @DbConfig ...
--       srvOpt = build @ServerConfig ...
--
--   main :: IO Config
--   main = do
--     db :* srv <- execOpt defaultSources configOpt
--     pure
--       $ runIdentity
--       $ Config
--       \<$\> construct db
--       \<*\> construct srv
-- @
--
-- * Subparsers
--
-- @
--   data OneConfig = OneConfig ...
--   data OtherConfig = OtherConfig ...
--
--   data Config
--     =  "one" :-> OneConfig
--     :+ "other" :-> OtherConfig
--
--   configOpt :: Config Opt
--   configOpt
--     = oneOpt :+ otherOpt :+ ANil
--     where
--       oneOpt = ...
--       otherOpt = ...
--
--   main :: IO ()
--   main = do
--     result <- execOpt defaultSources configOpt
--     case result of
--       HereF one            -> runWithOne one
--       ThereF (HereF other) -> runWithOther other
--     where
--       runWithOne :: One -> IO ()
--       runWithOne = ...
--       runWithOther :: Other -> IO ()
--       runWithOther = ...
-- @
--
-- TODO: more (and better) examples