{-# LANGUAGE AllowAmbiguousTypes #-} {-# LANGUAGE TemplateHaskellQuotes #-} module Hsm.Log.Options ( makeLoggerOptionParser ) where import Data.Proxy (Proxy(Proxy)) import GHC.TypeLits (symbolVal) import Generic.Data.Function.Common.Generic.Meta (KnownSymbols, symbolVals) import Hsm.Log (LoggerOptionPrefix, Severity) import Language.Haskell.TH ( Bang(Bang) , Body(NormalB) , Clause(Clause) , Con(RecC) , Dec(DataD, FunD, SigD) , Exp(AppE, ConE, LitE, ParensE, UInfixE, VarE) , Lit(StringL) , Name , Q , SourceStrictness(NoSourceStrictness) , SourceUnpackedness(NoSourceUnpackedness) , Type(AppT, ConT) , mkName ) import Options.Applicative (Parser, auto, help, long, metavar, option, showDefault, value) -- Generates an optparse-applicative parser for multiple logger severity levels -- -- Creates a record type with configurable log levels and a corresponding parser -- that accepts command-line arguments like `--log-cam` and `--log-web`. -- -- Example: -- -- $(makeLoggerOptionParser @'[ "cam", "web"] "Options" "parser" 'Info) -- -- Generates: -- * Record: `Options { logLevel_cam :: Severity, logLevel_web :: Severity }` -- * Parser: `parser :: Parser Options` with default values set to `Info` makeLoggerOptionParser :: forall ls. KnownSymbols ls => String -> String -> Name -> Q [Dec] makeLoggerOptionParser dataNameString parserNameString defaultSeverity = return [ DataD [] dataName [] Nothing [RecC dataName $ field <$> loggers] [] , SigD parserName $ ConT ''Parser `AppT` ConT dataName , FunD parserName [Clause [] (NormalB $ foldl parserApply parserBase loggers) []] ] where loggers = symbolVals @ls -- Record dataName = mkName dataNameString fieldPrefix = symbolVal $ Proxy @LoggerOptionPrefix fieldName logger = mkName $ fieldPrefix <> logger fieldBang = Bang NoSourceUnpackedness NoSourceStrictness fieldType = ConT ''Severity field logger = (fieldName logger, fieldBang, fieldType) -- Parser parserName = mkName parserNameString parserBase = VarE 'pure `AppE` ConE dataName parserConfigApply expr = UInfixE expr $ VarE '(<>) parserOptionLong logger = VarE 'long `AppE` LitE (StringL $ "log-" <> logger) parserOptionHelp logger = VarE 'help `AppE` LitE (StringL $ "Sets log level for logger " <> logger) parserOptionShowDefault = VarE 'showDefault parserOptionValue = VarE 'value `AppE` ConE defaultSeverity parserOptionMetavar = VarE 'metavar `AppE` LitE (StringL "LEVEL") parserOptions logger = [parserOptionLong logger, parserOptionHelp logger, parserOptionShowDefault, parserOptionValue, parserOptionMetavar] parserApply expr logger = UInfixE expr (VarE '(<*>)) $ VarE 'option `AppE` VarE 'auto `AppE` ParensE (foldl1 parserConfigApply $ parserOptions logger)