{-# LANGUAGE KindSignatures, DataKinds #-} {-# LANGUAGE FunctionalDependencies, FlexibleContexts #-} {-# LANGUAGE ScopedTypeVariables #-} -- | Because 'Flag' constructor isn't exposed, -- we have to explicitly 'toFlag' and 'fromFlag'. -- That way it's less likely we mix up bare Booleans. module Flag ( -- * Flag Flag, toFlag, fromFlag, -- * HasDefault HasDefault, -- * optparse-applicative showHide, switchM, ) where import Prelude () import Prelude.Compat import Control.Applicative ((<|>)) import Data.Semigroup (Semigroup (..)) import Data.Singletons.Bool import qualified Options.Applicative as O ------------------------------------------------------------------------------- -- Flag ------------------------------------------------------------------------------- newtype Flag t = Flag Bool toFlag :: t -> Bool -> Flag t toFlag _ = Flag fromFlag :: t -> Flag t -> Bool fromFlag _ (Flag b) = b ------------------------------------------------------------------------------- -- HasDefault ------------------------------------------------------------------------------- -- | Default value. -- -- With 'DeriveAnyClass' one could write -- -- @ -- data MyOpt = MyOpt deriving (HasDefault 'True) -- @ -- class SBoolI def => HasDefault (def :: Bool) t | t -> def def :: forall t def. HasDefault def t => t -> Flag t def t = toFlag t (reflectBool (P :: P def)) data P (def :: Bool) = P ------------------------------------------------------------------------------- -- optparse-applicative ------------------------------------------------------------------------------- showHide :: HasDefault def t => t -> String -> String -> O.Parser (Flag t) showHide t n d = O.flag' (toFlag t True) (O.long ("show-" ++ n) <> O.help d) <|> O.flag' (toFlag t False) (O.long ("hide-" ++ n)) <|> pure (def t) switchM :: HasDefault 'False t => t -> String -> String -> O.Parser (Flag t) switchM t n d = fmap (toFlag t) $ O.switch $ O.long n <> d' where d' | null d = mempty | otherwise = O.help d