{-# LANGUAGE CPP #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE UnicodeSyntax #-}
{-# OPTIONS_HADDOCK show-extensions #-}
module Configuration.Utils
(
ProgramInfo
, programInfo
, piDescription
, piHelpHeader
, piHelpFooter
, piOptionParser
, piDefaultConfiguration
, piConfigurationFiles
, ConfigValidation
, programInfoValidate
, runWithConfiguration
, PkgInfo
, runWithPkgInfoConfiguration
, parseConfiguration
, module Configuration.Utils.CommandLine
, module Configuration.Utils.ConfigFile
, module Configuration.Utils.Operators
, Lens'
, Lens
, module Configuration.Utils.Maybe
, module Configuration.Utils.Monoid
, ProgramInfoValidate
, piValidateConfiguration
, ConfigValidationFunction(..)
, piOptionParserAndDefaultConfiguration
) where
import Configuration.Utils.CommandLine
import Configuration.Utils.ConfigFile
import Configuration.Utils.Internal
import Configuration.Utils.Internal.JsonTools
import qualified Configuration.Utils.Internal.ConfigFileReader as CF
import Configuration.Utils.Maybe
import Configuration.Utils.Monoid
import Configuration.Utils.Operators
import Configuration.Utils.Validation
import Control.Monad (void, when)
import Control.Monad.Except (MonadError, throwError)
import Control.Monad.Writer (execWriterT, runWriterT)
import Control.Monad.IO.Class (MonadIO)
import qualified Data.ByteString.Char8 as B8
import qualified Data.CaseInsensitive as CI
import Data.Foldable
import Data.Maybe
import Data.Monoid.Unicode
import Data.String
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Data.Yaml as Yaml
import qualified Options.Applicative.Types as O
import qualified Options.Applicative as O
import Prelude hiding (any, concatMap, mapM_)
import Prelude.Unicode hiding ((×))
import System.IO
import qualified Prettyprinter as P
#ifdef REMOTE_CONFIGS
import Control.Monad.Trans.Control
#endif
newtype ConfigValidationFunction a f = ConfigValidationFunction
{ forall a (f :: * -> *).
ConfigValidationFunction a f -> ConfigValidation a f
runConfigValidation ∷ ConfigValidation a f
}
type ProgramInfo a = ProgramInfoValidate a []
data ProgramInfoValidate a f = ProgramInfo
{ forall a (f :: * -> *). ProgramInfoValidate a f -> String
_piDescription ∷ !String
, ∷ !(Maybe String)
, ∷ !(Maybe String)
, forall a (f :: * -> *). ProgramInfoValidate a f -> MParser a
_piOptionParser ∷ !(MParser a)
, forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ∷ !a
, forall a (f :: * -> *).
ProgramInfoValidate a f -> ConfigValidationFunction a f
_piValidateConfiguration ∷ !(ConfigValidationFunction a f)
, forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piConfigurationFiles ∷ ![ConfigFile]
}
piDescription ∷ Lens' (ProgramInfoValidate a f) String
piDescription :: forall a (f :: * -> *). Lens' (ProgramInfoValidate a f) String
piDescription = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> String
_piDescription forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s String
a → ProgramInfoValidate a f
s { _piDescription :: String
_piDescription = String
a }
{-# INLINE piDescription #-}
piHelpHeader ∷ Lens' (ProgramInfoValidate a f) (Maybe String)
= forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piHelpHeader forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s Maybe String
a → ProgramInfoValidate a f
s { _piHelpHeader :: Maybe String
_piHelpHeader = Maybe String
a }
{-# INLINE piHelpHeader #-}
piHelpFooter ∷ Lens' (ProgramInfoValidate a f) (Maybe String)
= forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piHelpFooter forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s Maybe String
a → ProgramInfoValidate a f
s { _piHelpFooter :: Maybe String
_piHelpFooter = Maybe String
a }
{-# INLINE piHelpFooter #-}
piOptionParser ∷ Lens' (ProgramInfoValidate a f) (MParser a)
piOptionParser :: forall a (f :: * -> *). Lens' (ProgramInfoValidate a f) (MParser a)
piOptionParser = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> MParser a
_piOptionParser forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s MParser a
a → ProgramInfoValidate a f
s { _piOptionParser :: MParser a
_piOptionParser = MParser a
a }
{-# INLINE piOptionParser #-}
piDefaultConfiguration ∷ Lens' (ProgramInfoValidate a f) a
piDefaultConfiguration :: forall a (f :: * -> *). Lens' (ProgramInfoValidate a f) a
piDefaultConfiguration = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s a
a → ProgramInfoValidate a f
s { _piDefaultConfiguration :: a
_piDefaultConfiguration = a
a }
{-# INLINE piDefaultConfiguration #-}
piValidateConfiguration ∷ Lens' (ProgramInfoValidate a f) (ConfigValidationFunction a f)
piValidateConfiguration :: forall a (f :: * -> *).
Lens' (ProgramInfoValidate a f) (ConfigValidationFunction a f)
piValidateConfiguration = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *).
ProgramInfoValidate a f -> ConfigValidationFunction a f
_piValidateConfiguration forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s ConfigValidationFunction a f
a → ProgramInfoValidate a f
s { _piValidateConfiguration :: ConfigValidationFunction a f
_piValidateConfiguration = ConfigValidationFunction a f
a }
{-# INLINE piValidateConfiguration #-}
piConfigurationFiles ∷ Lens' (ProgramInfoValidate a f) [ConfigFile]
piConfigurationFiles :: forall a (f :: * -> *).
Lens' (ProgramInfoValidate a f) [ConfigFile]
piConfigurationFiles = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piConfigurationFiles forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a f
s [ConfigFile]
a → ProgramInfoValidate a f
s { _piConfigurationFiles :: [ConfigFile]
_piConfigurationFiles = [ConfigFile]
a }
{-# INLINE piConfigurationFiles #-}
piOptionParserAndDefaultConfiguration
∷ Lens
(ProgramInfoValidate a b)
(ProgramInfoValidate c d)
(MParser a, a, ConfigValidationFunction a b)
(MParser c, c, ConfigValidationFunction c d)
piOptionParserAndDefaultConfiguration :: forall a (b :: * -> *) c (d :: * -> *).
Lens
(ProgramInfoValidate a b)
(ProgramInfoValidate c d)
(MParser a, a, ConfigValidationFunction a b)
(MParser c, c, ConfigValidationFunction c d)
piOptionParserAndDefaultConfiguration = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall {a} {f :: * -> *}.
ProgramInfoValidate a f
-> (MParser a, a, ConfigValidationFunction a f)
g forall a b. (a -> b) -> a -> b
$ \ProgramInfoValidate a b
s (MParser c
a,c
b,ConfigValidationFunction c d
c) → ProgramInfo
{ _piDescription :: String
_piDescription = forall a (f :: * -> *). ProgramInfoValidate a f -> String
_piDescription ProgramInfoValidate a b
s
, _piHelpHeader :: Maybe String
_piHelpHeader = forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piHelpHeader ProgramInfoValidate a b
s
, _piHelpFooter :: Maybe String
_piHelpFooter = forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piHelpFooter ProgramInfoValidate a b
s
, _piOptionParser :: MParser c
_piOptionParser = MParser c
a
, _piDefaultConfiguration :: c
_piDefaultConfiguration = c
b
, _piValidateConfiguration :: ConfigValidationFunction c d
_piValidateConfiguration = ConfigValidationFunction c d
c
, _piConfigurationFiles :: [ConfigFile]
_piConfigurationFiles = forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piConfigurationFiles ProgramInfoValidate a b
s
}
where
g :: ProgramInfoValidate a f
-> (MParser a, a, ConfigValidationFunction a f)
g ProgramInfoValidate a f
s = (forall a (f :: * -> *). ProgramInfoValidate a f -> MParser a
_piOptionParser ProgramInfoValidate a f
s, forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ProgramInfoValidate a f
s, forall a (f :: * -> *).
ProgramInfoValidate a f -> ConfigValidationFunction a f
_piValidateConfiguration ProgramInfoValidate a f
s)
{-# INLINE piOptionParserAndDefaultConfiguration #-}
programInfo
∷ String
→ MParser a
→ a
→ ProgramInfo a
programInfo :: forall a. String -> MParser a -> a -> ProgramInfo a
programInfo String
desc MParser a
parser a
defaultConfig =
forall a (f :: * -> *).
String
-> MParser a
-> a
-> ConfigValidation a f
-> ProgramInfoValidate a f
programInfoValidate String
desc MParser a
parser a
defaultConfig forall a b. (a -> b) -> a -> b
$ forall a b. a -> b -> a
const (forall (m :: * -> *) a. Monad m => a -> m a
return ())
programInfoValidate
∷ String
→ MParser a
→ a
→ ConfigValidation a f
→ ProgramInfoValidate a f
programInfoValidate :: forall a (f :: * -> *).
String
-> MParser a
-> a
-> ConfigValidation a f
-> ProgramInfoValidate a f
programInfoValidate String
desc MParser a
parser a
defaultConfig ConfigValidation a f
valFunc = ProgramInfo
{ _piDescription :: String
_piDescription = String
desc
, _piHelpHeader :: Maybe String
_piHelpHeader = forall a. Maybe a
Nothing
, _piHelpFooter :: Maybe String
_piHelpFooter = forall a. Maybe a
Nothing
, _piOptionParser :: MParser a
_piOptionParser = MParser a
parser
, _piDefaultConfiguration :: a
_piDefaultConfiguration = a
defaultConfig
, _piValidateConfiguration :: ConfigValidationFunction a f
_piValidateConfiguration = forall a (f :: * -> *).
ConfigValidation a f -> ConfigValidationFunction a f
ConfigValidationFunction ConfigValidation a f
valFunc
, _piConfigurationFiles :: [ConfigFile]
_piConfigurationFiles = []
}
data PrintConfigMode = Full | Minimal | Diff
printConfigModeToText ∷ PrintConfigMode → T.Text
printConfigModeToText :: PrintConfigMode -> Text
printConfigModeToText PrintConfigMode
Full = Text
"full"
printConfigModeToText PrintConfigMode
Minimal = Text
"minimal"
printConfigModeToText PrintConfigMode
Diff = Text
"diff"
printConfigModeFromText ∷ T.Text → Either String PrintConfigMode
printConfigModeFromText :: Text -> Either String PrintConfigMode
printConfigModeFromText Text
t = case forall s. FoldCase s => s -> CI s
CI.mk Text
t of
CI Text
"full" → forall a b. b -> Either a b
Right PrintConfigMode
Full
CI Text
"minimal" → forall a b. b -> Either a b
Right PrintConfigMode
Minimal
CI Text
"diff" → forall a b. b -> Either a b
Right PrintConfigMode
Diff
CI Text
x → forall a b. a -> Either a b
Left forall a b. (a -> b) -> a -> b
$ String
"unknow print configuration mode: " forall a. Semigroup a => a -> a -> a
<> forall a s. (Show a, IsString s) => a -> s
sshow CI Text
x
instance ToJSON PrintConfigMode where
toJSON :: PrintConfigMode -> Value
toJSON = forall a. ToJSON a => a -> Value
toJSON forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ PrintConfigMode -> Text
printConfigModeToText
{-# INLINE toJSON #-}
instance FromJSON PrintConfigMode where
parseJSON :: Value -> Parser PrintConfigMode
parseJSON = forall a. String -> (Text -> Parser a) -> Value -> Parser a
withText String
"PrintConfigMode"
forall a b. (a -> b) -> a -> b
$ forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either forall (m :: * -> *) a. MonadFail m => String -> m a
fail forall (m :: * -> *) a. Monad m => a -> m a
return forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ Text -> Either String PrintConfigMode
printConfigModeFromText
{-# INLINE parseJSON #-}
data AppConfiguration a = AppConfiguration
{ forall a. AppConfiguration a -> Maybe PrintConfigMode
_printConfig ∷ !(Maybe PrintConfigMode)
, forall a. AppConfiguration a -> ConfigFilesConfig
_configFilesConfig ∷ !ConfigFilesConfig
, forall a. AppConfiguration a -> [ConfigFile]
_configFiles ∷ ![ConfigFile]
, forall a. AppConfiguration a -> a
_mainConfig ∷ !a
}
configFiles ∷ Lens' (AppConfiguration a) [ConfigFile]
configFiles :: forall a. Lens' (AppConfiguration a) [ConfigFile]
configFiles = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a. AppConfiguration a -> [ConfigFile]
_configFiles forall a b. (a -> b) -> a -> b
$ \AppConfiguration a
s [ConfigFile]
a → AppConfiguration a
s { _configFiles :: [ConfigFile]
_configFiles = [ConfigFile]
a }
mainConfig ∷ Lens (AppConfiguration a) (AppConfiguration b) a b
mainConfig :: forall a b. Lens (AppConfiguration a) (AppConfiguration b) a b
mainConfig = forall s a b t. (s -> a) -> (s -> b -> t) -> Lens s t a b
lens forall a. AppConfiguration a -> a
_mainConfig forall a b. (a -> b) -> a -> b
$ \AppConfiguration a
s b
a → AppConfiguration a
s { _mainConfig :: b
_mainConfig = b
a }
pAppConfiguration
∷ O.Parser (a → a)
→ O.Parser (AppConfiguration (a → a))
pAppConfiguration :: forall a. Parser (a -> a) -> Parser (AppConfiguration (a -> a))
pAppConfiguration Parser (a -> a)
mainParser = forall a.
Maybe PrintConfigMode
-> ConfigFilesConfig -> [ConfigFile] -> a -> AppConfiguration a
AppConfiguration
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser (Maybe PrintConfigMode)
pPrintConfig
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (MParser ConfigFilesConfig
pConfigFilesConfig forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Applicative f => a -> f a
pure ConfigFilesConfig
defaultConfigFilesConfig)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Alternative f => f a -> f [a]
many Parser ConfigFile
pConfigFile
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser (a -> a)
mainParser
where
pConfigFile :: Parser ConfigFile
pConfigFile = Text -> ConfigFile
ConfigFileRequired forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ String -> Text
T.pack forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall s. IsString s => Mod OptionFields s -> Parser s
O.strOption
forall a b. (a -> b) -> a -> b
% forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"config-file"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
O.metavar String
"FILE"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Configuration file in YAML or JSON format. If more than a single config file option is present files are loaded in the order in which they appear on the command line."
pPrintConfig :: Parser (Maybe PrintConfigMode)
pPrintConfig
= forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser PrintConfigMode
pPrintConfigOption
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall a. a -> Maybe a
Just forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser PrintConfigMode
pPrintConfigFlag
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. Maybe a
Nothing
pPrintConfigFlag :: Parser PrintConfigMode
pPrintConfigFlag = forall a. a -> Mod FlagFields a -> Parser a
O.flag' PrintConfigMode
Full
forall a b. (a -> b) -> a -> b
% forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"print-config"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print the parsed configuration to standard out and exit. This is an alias for --print-config-as=full"
pPrintConfigOption :: Parser PrintConfigMode
pPrintConfigOption = forall a. ReadM a -> Mod OptionFields a -> Parser a
O.option (forall a. (String -> Either String a) -> ReadM a
eitherReader forall a b. (a -> b) -> a -> b
$ Text -> Either String PrintConfigMode
printConfigModeFromText forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
. String -> Text
T.pack)
forall a b. (a -> b) -> a -> b
% forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"print-config-as"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print the parsed configuration to standard out and exit"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasCompleter f => [String] -> Mod f a
O.completeWith [String
"full", String
"minimal", String
"diff", String
"Full", String
"Minimal", String
"Diff"]
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasMetavar f => String -> Mod f a
O.metavar String
"full|minimal|diff"
runWithConfiguration
∷ (FromJSON (a → a), ToJSON a, Foldable f, Monoid (f T.Text))
⇒ ProgramInfoValidate a f
→ (a → IO ())
→ IO ()
runWithConfiguration :: forall a (f :: * -> *).
(FromJSON (a -> a), ToJSON a, Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f -> (a -> IO ()) -> IO ()
runWithConfiguration ProgramInfoValidate a f
appInfo = forall a (f :: * -> *).
(FromJSON (a -> a), ToJSON a, Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b)) -> (a -> IO ()) -> IO ()
runInternal ProgramInfoValidate a f
appInfo forall a. Maybe a
Nothing
pPkgInfo ∷ PkgInfo → MParser a
pPkgInfo :: forall a. PkgInfo -> MParser a
pPkgInfo (String
sinfo, String
detailedInfo, String
version, String
license) =
forall {a}. Parser (a -> a)
infoO forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall {a}. Parser (a -> a)
detailedInfoO forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall {a}. Parser (a -> a)
versionO forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall {a}. Parser (a -> a)
licenseO
where
infoO :: Parser (a -> a)
infoO = forall a. String -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption String
sinfo
forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"info"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print program info message and exit"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasValue f => a -> Mod f a
O.value forall a. a -> a
id
detailedInfoO :: Parser (a -> a)
detailedInfoO = forall a. String -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption String
detailedInfo
forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"long-info"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print detailed program info message and exit"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasValue f => a -> Mod f a
O.value forall a. a -> a
id
versionO :: Parser (a -> a)
versionO = forall a. String -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption String
version
forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"version"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasName f => Char -> Mod f a
O.short Char
'v'
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print version string and exit"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasValue f => a -> Mod f a
O.value forall a. a -> a
id
licenseO :: Parser (a -> a)
licenseO = forall a. String -> Mod OptionFields (a -> a) -> Parser (a -> a)
infoOption String
license
forall a b. (a -> b) -> a -> b
$ forall (f :: * -> *) a. HasName f => String -> Mod f a
O.long String
"license"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
O.help String
"Print license of the program and exit"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasValue f => a -> Mod f a
O.value forall a. a -> a
id
type PkgInfo =
( String
, String
, String
, String
)
runWithPkgInfoConfiguration
∷ (FromJSON (a → a), ToJSON a, Foldable f, Monoid (f T.Text))
⇒ ProgramInfoValidate a f
→ PkgInfo
→ (a → IO ())
→ IO ()
runWithPkgInfoConfiguration :: forall a (f :: * -> *).
(FromJSON (a -> a), ToJSON a, Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f -> PkgInfo -> (a -> IO ()) -> IO ()
runWithPkgInfoConfiguration ProgramInfoValidate a f
appInfo PkgInfo
pkgInfo =
forall a (f :: * -> *).
(FromJSON (a -> a), ToJSON a, Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b)) -> (a -> IO ()) -> IO ()
runInternal ProgramInfoValidate a f
appInfo (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall a. PkgInfo -> MParser a
pPkgInfo PkgInfo
pkgInfo)
mainOptions
∷ ∀ a f . FromJSON (a → a)
⇒ ProgramInfoValidate a f
→ (∀ b . Maybe (MParser b))
→ O.ParserInfo (AppConfiguration (a → a))
mainOptions :: forall a (f :: * -> *).
FromJSON (a -> a) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b))
-> ParserInfo (AppConfiguration (a -> a))
mainOptions ProgramInfo{a
String
[ConfigFile]
Maybe String
MParser a
ConfigValidationFunction a f
_piConfigurationFiles :: [ConfigFile]
_piValidateConfiguration :: ConfigValidationFunction a f
_piDefaultConfiguration :: a
_piOptionParser :: MParser a
_piHelpFooter :: Maybe String
_piHelpHeader :: Maybe String
_piDescription :: String
_piConfigurationFiles :: forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piValidateConfiguration :: forall a (f :: * -> *).
ProgramInfoValidate a f -> ConfigValidationFunction a f
_piDefaultConfiguration :: forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piOptionParser :: forall a (f :: * -> *). ProgramInfoValidate a f -> MParser a
_piHelpFooter :: forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piHelpHeader :: forall a (f :: * -> *). ProgramInfoValidate a f -> Maybe String
_piDescription :: forall a (f :: * -> *). ProgramInfoValidate a f -> String
..} forall b. Maybe (MParser b)
pkgInfoParser = forall a. Parser a -> InfoMod a -> ParserInfo a
O.info Parser (AppConfiguration (a -> a))
optionParser
forall a b. (a -> b) -> a -> b
$ forall a. String -> InfoMod a
O.progDesc String
_piDescription
forall α. Monoid α => α -> α -> α
⊕ forall a. InfoMod a
O.fullDesc
forall α. Monoid α => α -> α -> α
⊕ forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. Monoid a => a
mempty forall a. String -> InfoMod a
O.header Maybe String
_piHelpHeader
forall α. Monoid α => α -> α -> α
⊕ forall a. Maybe Doc -> InfoMod a
O.footerDoc (forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ forall {ann}. Doc ann
defaultFooter forall α. Monoid α => α -> α -> α
⊕ forall b a. b -> (a -> b) -> Maybe a -> b
maybe forall a. Monoid a => a
mempty forall a ann. Pretty a => a -> Doc ann
P.pretty Maybe String
_piHelpFooter)
where
optionParser :: Parser (AppConfiguration (a -> a))
optionParser =
forall a. a -> Maybe a -> a
fromMaybe (forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a. a -> a
id) forall b. Maybe (MParser b)
pkgInfoParser forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall {a}. Parser (a -> a)
nonHiddenHelper
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. Parser (a -> a) -> Parser (AppConfiguration (a -> a))
pAppConfiguration MParser a
_piOptionParser
nonHiddenHelper :: Parser (a -> a)
nonHiddenHelper = forall a.
ParseError -> Mod OptionFields (a -> a) -> Parser (a -> a)
abortOption (Maybe String -> ParseError
ShowHelpText forall a. Maybe a
Nothing)
forall a b. (a -> b) -> a -> b
% forall (f :: * -> *) a. HasName f => String -> Mod f a
long String
"help"
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'h'
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'?'
forall α. Monoid α => α -> α -> α
⊕ forall (f :: * -> *) a. String -> Mod f a
help String
"Show this help message"
defaultFooter :: Doc ann
defaultFooter = forall ann. [Doc ann] -> Doc ann
P.vsep
[ forall {ann}. String -> Doc ann
par String
"Configurations are loaded in order from the following sources:"
, forall ann. Int -> Doc ann -> Doc ann
P.indent Int
2 forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall ann. [Doc ann] -> Doc ann
P.vsep forall a b. (a -> b) -> a -> b
$ forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith forall a b. (a -> b) -> a -> b
($) (forall a. [Maybe a] -> [a]
catMaybes [forall {ann}. Maybe (Int -> Doc ann)
staticFiles, forall {ann}. Maybe (Int -> Doc ann)
cmdFiles, forall {ann}. Maybe (Int -> Doc ann)
cmdOptions]) [Int
1..]
, Doc ann
""
, forall ann. [Doc ann] -> Doc ann
P.fillSep
[ forall {ann}. String -> Doc ann
par String
"Configuration file locations can be either local file system paths"
, forall {ann}. String -> Doc ann
par String
"or remote HTTP or HTTPS URLs. Remote URLs must start with"
, forall {ann}. String -> Doc ann
par String
"either \"http://\" or \"https://\"."
]
, Doc ann
""
, forall ann. [Doc ann] -> Doc ann
P.fillSep
[ forall {ann}. String -> Doc ann
par String
"Configuration settings that are loaded later overwrite settings"
, forall {ann}. String -> Doc ann
par String
"that were loaded before."
]
, Doc ann
""
]
Doc ann
a </> :: Doc ann -> Doc ann -> Doc ann
</> Doc ann
b = Doc ann
a forall a. Semigroup a => a -> a -> a
<> forall {ann}. Doc ann
P.softline forall a. Semigroup a => a -> a -> a
<> Doc ann
b
staticFiles :: Maybe (Int -> Doc ann)
staticFiles
| forall (t :: * -> *) a. Foldable t => t a -> Bool
null [ConfigFile]
_piConfigurationFiles = forall a. Maybe a
Nothing
| Bool
otherwise = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \Int
n → forall ann. Int -> Doc ann -> Doc ann
P.hang Int
3 forall a b. (a -> b) -> a -> b
$ forall ann. [Doc ann] -> Doc ann
P.vsep
[ forall a ann. Pretty a => a -> Doc ann
P.pretty @Int Int
n forall α. Monoid α => α -> α -> α
⊕ Doc ann
"." forall {ann}. Doc ann -> Doc ann -> Doc ann
</> forall {ann}. String -> Doc ann
par String
"Configuration files at the following locations:"
, forall ann. [Doc ann] -> Doc ann
P.vsep forall a b. (a -> b) -> a -> b
$ forall a b. (a -> b) -> [a] -> [b]
map (\ConfigFile
f → Doc ann
"* " forall α. Monoid α => α -> α -> α
⊕ forall {ann}. ConfigFile -> Doc ann
printConfigFile ConfigFile
f) [ConfigFile]
_piConfigurationFiles
]
cmdFiles :: Maybe (Int -> Doc ann)
cmdFiles = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \Int
n → forall ann. Int -> Doc ann -> Doc ann
P.hang Int
3 forall a b. (a -> b) -> a -> b
$ forall ann. [Doc ann] -> Doc ann
P.fillSep
[ forall a ann. Pretty a => a -> Doc ann
P.pretty Int
n forall α. Monoid α => α -> α -> α
⊕ Doc ann
"." forall {ann}. Doc ann -> Doc ann -> Doc ann
</> forall {ann}. String -> Doc ann
par String
"Configuration files from locations provided through"
, forall {ann}. String -> Doc ann
par String
"--config-file options in the order as they appear."
]
cmdOptions :: Maybe (Int -> Doc ann)
cmdOptions = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ \Int
n → forall ann. Int -> Doc ann -> Doc ann
P.hang Int
3
forall a b. (a -> b) -> a -> b
$ forall a ann. Pretty a => a -> Doc ann
P.pretty Int
n forall α. Monoid α => α -> α -> α
⊕ Doc ann
"." forall {ann}. Doc ann -> Doc ann -> Doc ann
</> forall {ann}. String -> Doc ann
par String
"Command line options."
printConfigFile :: ConfigFile -> Doc ann
printConfigFile ConfigFile
f = forall a ann. Pretty a => a -> Doc ann
P.pretty (ConfigFile -> Text
getConfigFile ConfigFile
f) forall {ann}. Doc ann -> Doc ann -> Doc ann
P.<+> case ConfigFile
f of
ConfigFileRequired Text
_ → Doc ann
"(required)"
ConfigFileOptional Text
_ → Doc ann
"(optional)"
par :: String -> Doc ann
par = forall ann. [Doc ann] -> Doc ann
P.fillSep forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a b. (a -> b) -> [a] -> [b]
map forall a ann. Pretty a => a -> Doc ann
P.pretty forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ String -> [String]
words
runInternal
∷ (FromJSON (a → a), ToJSON a, Foldable f, Monoid (f T.Text))
⇒ ProgramInfoValidate a f
→ (∀ b . Maybe (MParser b))
→ (a → IO ())
→ IO ()
runInternal :: forall a (f :: * -> *).
(FromJSON (a -> a), ToJSON a, Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b)) -> (a -> IO ()) -> IO ()
runInternal ProgramInfoValidate a f
appInfo forall b. Maybe (MParser b)
maybePkgInfo a -> IO ()
mainFunction = do
AppConfiguration (a -> a)
cliAppConf ← forall a. Lens' (AppConfiguration a) [ConfigFile]
configFiles forall a b s t.
((a -> Identity b) -> s -> Identity t) -> (a -> b) -> s -> t
`over` forall α. Monoid α => α -> α -> α
(⊕) (forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piConfigurationFiles ProgramInfoValidate a f
appInfo) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$>
forall a. ParserPrefs -> ParserInfo a -> IO a
O.customExecParser ParserPrefs
parserPrefs (forall a (f :: * -> *).
FromJSON (a -> a) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b))
-> ParserInfo (AppConfiguration (a -> a))
mainOptions ProgramInfoValidate a f
appInfo forall b. Maybe (MParser b)
maybePkgInfo)
AppConfiguration a
appConf ← AppConfiguration (a -> a)
cliAppConf forall a b. a -> (a -> b) -> b
& forall a b. Lens (AppConfiguration a) (AppConfiguration b) a b
mainConfig forall a. a -> a
`id` \a -> a
a → a -> a
a forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a. Monad m => ExceptT Text m a -> m a
errorT forall a b. (a -> b) -> a -> b
% forall (m :: * -> *) a.
(ConfigFileParser m, FromJSON (a -> a)) =>
ConfigFilesConfig -> a -> [ConfigFile] -> m a
CF.parseConfigFiles
(forall a. AppConfiguration a -> ConfigFilesConfig
_configFilesConfig AppConfiguration (a -> a)
cliAppConf)
(forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ProgramInfoValidate a f
appInfo)
(forall a. AppConfiguration a -> [ConfigFile]
_configFiles AppConfiguration (a -> a)
cliAppConf)
forall (f :: * -> *) a.
(Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f -> a -> IO ()
validateConfig ProgramInfoValidate a f
appInfo forall a b. (a -> b) -> a -> b
$ forall a. AppConfiguration a -> a
_mainConfig AppConfiguration a
appConf
case forall a. AppConfiguration a -> Maybe PrintConfigMode
_printConfig AppConfiguration a
appConf of
Maybe PrintConfigMode
Nothing → a -> IO ()
mainFunction forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. AppConfiguration a -> a
_mainConfig forall a b. (a -> b) -> a -> b
$ AppConfiguration a
appConf
Just PrintConfigMode
Full → ByteString -> IO ()
B8.putStrLn forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. ToJSON a => a -> ByteString
Yaml.encode forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. AppConfiguration a -> a
_mainConfig forall a b. (a -> b) -> a -> b
$ AppConfiguration a
appConf
Just PrintConfigMode
Minimal → ByteString -> IO ()
B8.putStrLn
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. ToJSON a => a -> ByteString
Yaml.encode
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ (Diff Value -> Value) -> Value -> Value
resolve Diff Value -> Value
resolveOnlyRight
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ Value -> Value -> Value
diff (forall a. ToJSON a => a -> Value
toJSON forall a b. (a -> b) -> a -> b
$ forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ProgramInfoValidate a f
appInfo)
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. ToJSON a => a -> Value
toJSON
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. AppConfiguration a -> a
_mainConfig
forall a b. (a -> b) -> a -> b
$ AppConfiguration a
appConf
Just PrintConfigMode
Diff → ByteString -> IO ()
B8.putStrLn
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. ToJSON a => a -> ByteString
Yaml.encode
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ Value -> Value -> Value
diff (forall a. ToJSON a => a -> Value
toJSON forall a b. (a -> b) -> a -> b
$ forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ProgramInfoValidate a f
appInfo)
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. ToJSON a => a -> Value
toJSON
forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a. AppConfiguration a -> a
_mainConfig
forall a b. (a -> b) -> a -> b
$ AppConfiguration a
appConf
where
parserPrefs :: ParserPrefs
parserPrefs = PrefsMod -> ParserPrefs
O.prefs forall a. Monoid a => a
mempty
parseConfiguration
∷
( Applicative m
, MonadIO m
#ifdef REMOTE_CONFIGS
, MonadBaseControl IO m
#endif
, MonadError T.Text m
, FromJSON (a → a)
, ToJSON a
, Foldable f
, Monoid (f T.Text)
)
⇒ T.Text
→ ProgramInfoValidate a f
→ [String]
→ m a
parseConfiguration :: forall (m :: * -> *) a (f :: * -> *).
(Applicative m, MonadIO m, MonadError Text m, FromJSON (a -> a),
ToJSON a, Foldable f, Monoid (f Text)) =>
Text -> ProgramInfoValidate a f -> [String] -> m a
parseConfiguration Text
appName ProgramInfoValidate a f
appInfo [String]
args = do
AppConfiguration (a -> a)
cliAppConf ← case forall a. ParserPrefs -> ParserInfo a -> [String] -> ParserResult a
O.execParserPure ParserPrefs
parserPrefs (forall a (f :: * -> *).
FromJSON (a -> a) =>
ProgramInfoValidate a f
-> (forall b. Maybe (MParser b))
-> ParserInfo (AppConfiguration (a -> a))
mainOptions ProgramInfoValidate a f
appInfo forall a. Maybe a
Nothing) [String]
args of
O.Success AppConfiguration (a -> a)
a → forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ AppConfiguration (a -> a)
a forall a b. a -> (a -> b) -> b
& forall a. Lens' (AppConfiguration a) [ConfigFile]
configFiles forall a b s t.
((a -> Identity b) -> s -> Identity t) -> (a -> b) -> s -> t
`over` forall α. Monoid α => α -> α -> α
(⊕) (forall a (f :: * -> *). ProgramInfoValidate a f -> [ConfigFile]
_piConfigurationFiles ProgramInfoValidate a f
appInfo)
O.Failure ParserFailure ParserHelp
e → forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ String -> Text
T.pack forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall a b. (a, b) -> a
fst forall a b. (a -> b) -> a -> b
$ ParserFailure ParserHelp -> String -> (String, ExitCode)
renderFailure ParserFailure ParserHelp
e (Text -> String
T.unpack Text
appName)
O.CompletionInvoked CompletionResult
_ → forall e (m :: * -> *) a. MonadError e m => e -> m a
throwError Text
"command line parser returned completion result"
AppConfiguration a
appConf ← AppConfiguration (a -> a)
cliAppConf forall a b. a -> (a -> b) -> b
& forall a b. Lens (AppConfiguration a) (AppConfiguration b) a b
mainConfig forall a. a -> a
`id` \a -> a
a → a -> a
a forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (m :: * -> *) a.
(ConfigFileParser m, FromJSON (a -> a)) =>
ConfigFilesConfig -> a -> [ConfigFile] -> m a
CF.parseConfigFiles
(forall a. AppConfiguration a -> ConfigFilesConfig
_configFilesConfig AppConfiguration (a -> a)
cliAppConf)
(forall a (f :: * -> *). ProgramInfoValidate a f -> a
_piDefaultConfiguration ProgramInfoValidate a f
appInfo)
(forall a. AppConfiguration a -> [ConfigFile]
_configFiles AppConfiguration (a -> a)
cliAppConf)
forall (f :: * -> *) a. Functor f => f a -> f ()
void forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall {f :: * -> *} {m :: * -> *} {a}.
(Monoid (f Text), MonadIO m, MonadError Text m) =>
ProgramInfoValidate a f -> a -> m ((), f Text)
validate ProgramInfoValidate a f
appInfo forall a b. (a -> b) -> a -> b
$ forall a. AppConfiguration a -> a
_mainConfig AppConfiguration a
appConf
forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$ forall a. AppConfiguration a -> a
_mainConfig AppConfiguration a
appConf
where
parserPrefs :: ParserPrefs
parserPrefs = PrefsMod -> ParserPrefs
O.prefs PrefsMod
O.disambiguate
validate :: ProgramInfoValidate a f -> a -> m ((), f Text)
validate ProgramInfoValidate a f
i a
conf = forall w (m :: * -> *) a. WriterT w m a -> m (a, w)
runWriterT forall a b. (a -> b) -> a -> b
$
forall a (f :: * -> *).
ConfigValidationFunction a f -> ConfigValidation a f
runConfigValidation (forall r (m :: * -> *) a.
MonadReader r m =>
((a -> Const a a) -> r -> Const a r) -> m a
view forall a (f :: * -> *).
Lens' (ProgramInfoValidate a f) (ConfigValidationFunction a f)
piValidateConfiguration ProgramInfoValidate a f
i) a
conf
validateConfig
∷ (Foldable f, Monoid (f T.Text))
⇒ ProgramInfoValidate a f
→ a
→ IO ()
validateConfig :: forall (f :: * -> *) a.
(Foldable f, Monoid (f Text)) =>
ProgramInfoValidate a f -> a -> IO ()
validateConfig ProgramInfoValidate a f
appInfo a
conf = do
f Text
warnings ← forall (m :: * -> *) w a. Monad m => WriterT w m a -> m w
execWriterT forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ forall (m :: * -> *) e b a.
Monad m =>
(e -> m b) -> (a -> m b) -> ExceptT e m a -> m b
exceptT (forall a. HasCallStack => String -> a
error forall β γ α. (β -> γ) -> (α -> β) -> α -> γ
∘ Text -> String
T.unpack) forall (m :: * -> *) a. Monad m => a -> m a
return forall a b. (a -> b) -> a -> b
$
forall a (f :: * -> *).
ConfigValidationFunction a f -> ConfigValidation a f
runConfigValidation (forall r (m :: * -> *) a.
MonadReader r m =>
((a -> Const a a) -> r -> Const a r) -> m a
view forall a (f :: * -> *).
Lens' (ProgramInfoValidate a f) (ConfigValidationFunction a f)
piValidateConfiguration ProgramInfoValidate a f
appInfo) a
conf
forall (f :: * -> *). Applicative f => Bool -> f () -> f ()
when (forall (t :: * -> *) a. Foldable t => (a -> Bool) -> t a -> Bool
any (forall a b. a -> b -> a
const Bool
True) f Text
warnings) forall a b. (a -> b) -> a -> b
$ do
Handle -> Text -> IO ()
T.hPutStrLn Handle
stderr Text
"WARNINGS:"
forall (t :: * -> *) (m :: * -> *) a b.
(Foldable t, Monad m) =>
(a -> m b) -> t a -> m ()
mapM_ (\Text
w → Handle -> Text -> IO ()
T.hPutStrLn Handle
stderr forall a b. (a -> b) -> a -> b
$ Text
"warning: " forall α. Monoid α => α -> α -> α
⊕ Text
w) f Text
warnings