module Options.Applicative.Builder.Internal (
  -- * Internals
  Mod(..),
  HasName(..),
  HasCompleter(..),
  HasValue(..),
  HasMetavar(..),
  OptionFields(..),
  FlagFields(..),
  CommandFields(..),
  ArgumentFields(..),
  DefaultProp(..),

  optionMod,
  fieldMod,

  baseProps,
  mkCommand,
  mkParser,
  mkOption,
  mkProps,

  internal,
  noGlobal
  ) where

import Control.Applicative
import Control.Monad (mplus)
import Data.Semigroup hiding (Option)
import Prelude

import Options.Applicative.Common
import Options.Applicative.Types

data OptionFields a = OptionFields
  { OptionFields a -> [OptName]
optNames :: [OptName]
  , OptionFields a -> Completer
optCompleter :: Completer
  , OptionFields a -> String -> ParseError
optNoArgError :: String -> ParseError }

data FlagFields a = FlagFields
  { FlagFields a -> [OptName]
flagNames :: [OptName]
  , FlagFields a -> a
flagActive :: a }

data CommandFields a = CommandFields
  { CommandFields a -> [(String, ParserInfo a)]
cmdCommands :: [(String, ParserInfo a)]
  , CommandFields a -> Maybe String
cmdGroup :: Maybe String }

data ArgumentFields a = ArgumentFields
  { ArgumentFields a -> Completer
argCompleter :: Completer }

class HasName f where
  name :: OptName -> f a -> f a

instance HasName OptionFields where
  name :: OptName -> OptionFields a -> OptionFields a
name OptName
n OptionFields a
fields = OptionFields a
fields { optNames :: [OptName]
optNames = OptName
n OptName -> [OptName] -> [OptName]
forall a. a -> [a] -> [a]
: OptionFields a -> [OptName]
forall a. OptionFields a -> [OptName]
optNames OptionFields a
fields }

instance HasName FlagFields where
  name :: OptName -> FlagFields a -> FlagFields a
name OptName
n FlagFields a
fields = FlagFields a
fields { flagNames :: [OptName]
flagNames = OptName
n OptName -> [OptName] -> [OptName]
forall a. a -> [a] -> [a]
: FlagFields a -> [OptName]
forall a. FlagFields a -> [OptName]
flagNames FlagFields a
fields }

class HasCompleter f where
  modCompleter :: (Completer -> Completer) -> f a -> f a

instance HasCompleter OptionFields where
  modCompleter :: (Completer -> Completer) -> OptionFields a -> OptionFields a
modCompleter Completer -> Completer
f OptionFields a
p = OptionFields a
p { optCompleter :: Completer
optCompleter = Completer -> Completer
f (OptionFields a -> Completer
forall a. OptionFields a -> Completer
optCompleter OptionFields a
p) }

instance HasCompleter ArgumentFields where
  modCompleter :: (Completer -> Completer) -> ArgumentFields a -> ArgumentFields a
modCompleter Completer -> Completer
f ArgumentFields a
p = ArgumentFields a
p { argCompleter :: Completer
argCompleter = Completer -> Completer
f (ArgumentFields a -> Completer
forall a. ArgumentFields a -> Completer
argCompleter ArgumentFields a
p) }

class HasValue f where
  -- this is just so that it is not necessary to specify the kind of f
  hasValueDummy :: f a -> ()
instance HasValue OptionFields where
  hasValueDummy :: OptionFields a -> ()
hasValueDummy OptionFields a
_ = ()
instance HasValue ArgumentFields where
  hasValueDummy :: ArgumentFields a -> ()
hasValueDummy ArgumentFields a
_ = ()

class HasMetavar f where
  hasMetavarDummy :: f a -> ()
instance HasMetavar OptionFields where
  hasMetavarDummy :: OptionFields a -> ()
hasMetavarDummy OptionFields a
_ = ()
instance HasMetavar ArgumentFields where
  hasMetavarDummy :: ArgumentFields a -> ()
hasMetavarDummy ArgumentFields a
_ = ()
instance HasMetavar CommandFields where
  hasMetavarDummy :: CommandFields a -> ()
hasMetavarDummy CommandFields a
_ = ()

-- mod --

data DefaultProp a = DefaultProp
  (Maybe a)
  (Maybe (a -> String))

instance Monoid (DefaultProp a) where
  mempty :: DefaultProp a
mempty = Maybe a -> Maybe (a -> String) -> DefaultProp a
forall a. Maybe a -> Maybe (a -> String) -> DefaultProp a
DefaultProp Maybe a
forall a. Maybe a
Nothing Maybe (a -> String)
forall a. Maybe a
Nothing
  mappend :: DefaultProp a -> DefaultProp a -> DefaultProp a
mappend = DefaultProp a -> DefaultProp a -> DefaultProp a
forall a. Semigroup a => a -> a -> a
(<>)

instance Semigroup (DefaultProp a) where
  (DefaultProp Maybe a
d1 Maybe (a -> String)
s1) <> :: DefaultProp a -> DefaultProp a -> DefaultProp a
<> (DefaultProp Maybe a
d2 Maybe (a -> String)
s2) =
    Maybe a -> Maybe (a -> String) -> DefaultProp a
forall a. Maybe a -> Maybe (a -> String) -> DefaultProp a
DefaultProp (Maybe a
d1 Maybe a -> Maybe a -> Maybe a
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Maybe a
d2) (Maybe (a -> String)
s1 Maybe (a -> String) -> Maybe (a -> String) -> Maybe (a -> String)
forall (m :: * -> *) a. MonadPlus m => m a -> m a -> m a
`mplus` Maybe (a -> String)
s2)

-- | An option modifier.
--
-- Option modifiers are values that represent a modification of the properties
-- of an option.
--
-- The type parameter @a@ is the return type of the option, while @f@ is a
-- record containing its properties (e.g. 'OptionFields' for regular options,
-- 'FlagFields' for flags, etc...).
--
-- An option modifier consists of 3 elements:
--
--  - A field modifier, of the form @f a -> f a@. These are essentially
--  (compositions of) setters for some of the properties supported by @f@.
--
--  - An optional default value and function to display it.
--
--  - A property modifier, of the form @OptProperties -> OptProperties@. This
--  is just like the field modifier, but for properties applicable to any
--  option.
--
-- Modifiers are instances of 'Monoid', and can be composed as such.
--
-- One rarely needs to deal with modifiers directly, as most of the times it is
-- sufficient to pass them to builders (such as 'strOption' or 'flag') to
-- create options (see 'Options.Applicative.Builder').
data Mod f a = Mod (f a -> f a)
                   (DefaultProp a)
                   (OptProperties -> OptProperties)

optionMod :: (OptProperties -> OptProperties) -> Mod f a
optionMod :: (OptProperties -> OptProperties) -> Mod f a
optionMod = (f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a.
(f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
Mod f a -> f a
forall a. a -> a
id DefaultProp a
forall a. Monoid a => a
mempty

fieldMod :: (f a -> f a) -> Mod f a
fieldMod :: (f a -> f a) -> Mod f a
fieldMod f a -> f a
f = (f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a.
(f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
Mod f a -> f a
f DefaultProp a
forall a. Monoid a => a
mempty OptProperties -> OptProperties
forall a. a -> a
id

instance Monoid (Mod f a) where
  mempty :: Mod f a
mempty = (f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a.
(f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
Mod f a -> f a
forall a. a -> a
id DefaultProp a
forall a. Monoid a => a
mempty OptProperties -> OptProperties
forall a. a -> a
id
  mappend :: Mod f a -> Mod f a -> Mod f a
mappend = Mod f a -> Mod f a -> Mod f a
forall a. Semigroup a => a -> a -> a
(<>)

-- | @since 0.13.0.0
instance Semigroup (Mod f a) where
  Mod f a -> f a
f1 DefaultProp a
d1 OptProperties -> OptProperties
g1 <> :: Mod f a -> Mod f a -> Mod f a
<> Mod f a -> f a
f2 DefaultProp a
d2 OptProperties -> OptProperties
g2
    = (f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a.
(f a -> f a)
-> DefaultProp a -> (OptProperties -> OptProperties) -> Mod f a
Mod (f a -> f a
f2 (f a -> f a) -> (f a -> f a) -> f a -> f a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. f a -> f a
f1) (DefaultProp a
d2 DefaultProp a -> DefaultProp a -> DefaultProp a
forall a. Semigroup a => a -> a -> a
<> DefaultProp a
d1) (OptProperties -> OptProperties
g2 (OptProperties -> OptProperties)
-> (OptProperties -> OptProperties)
-> OptProperties
-> OptProperties
forall b c a. (b -> c) -> (a -> b) -> a -> c
. OptProperties -> OptProperties
g1)

-- | Base default properties.
baseProps :: OptProperties
baseProps :: OptProperties
baseProps = OptProperties :: OptVisibility
-> Chunk Doc
-> String
-> Maybe String
-> Bool
-> Maybe (Doc -> Doc)
-> OptProperties
OptProperties
  { propMetaVar :: String
propMetaVar = String
""
  , propVisibility :: OptVisibility
propVisibility = OptVisibility
Visible
  , propHelp :: Chunk Doc
propHelp = Chunk Doc
forall a. Monoid a => a
mempty
  , propShowDefault :: Maybe String
propShowDefault = Maybe String
forall a. Maybe a
Nothing
  , propDescMod :: Maybe (Doc -> Doc)
propDescMod = Maybe (Doc -> Doc)
forall a. Maybe a
Nothing
  , propShowGlobal :: Bool
propShowGlobal = Bool
True
  }

mkCommand :: Mod CommandFields a -> (Maybe String, [String], String -> Maybe (ParserInfo a))
mkCommand :: Mod CommandFields a
-> (Maybe String, [String], String -> Maybe (ParserInfo a))
mkCommand Mod CommandFields a
m = (Maybe String
group, ((String, ParserInfo a) -> String)
-> [(String, ParserInfo a)] -> [String]
forall a b. (a -> b) -> [a] -> [b]
map (String, ParserInfo a) -> String
forall a b. (a, b) -> a
fst [(String, ParserInfo a)]
cmds, (String -> [(String, ParserInfo a)] -> Maybe (ParserInfo a)
forall a b. Eq a => a -> [(a, b)] -> Maybe b
`lookup` [(String, ParserInfo a)]
cmds))
  where
    Mod CommandFields a -> CommandFields a
f DefaultProp a
_ OptProperties -> OptProperties
_ = Mod CommandFields a
m
    CommandFields [(String, ParserInfo a)]
cmds Maybe String
group = CommandFields a -> CommandFields a
f ([(String, ParserInfo a)] -> Maybe String -> CommandFields a
forall a.
[(String, ParserInfo a)] -> Maybe String -> CommandFields a
CommandFields [] Maybe String
forall a. Maybe a
Nothing)

mkParser :: DefaultProp a
         -> (OptProperties -> OptProperties)
         -> OptReader a
         -> Parser a
mkParser :: DefaultProp a
-> (OptProperties -> OptProperties) -> OptReader a -> Parser a
mkParser d :: DefaultProp a
d@(DefaultProp Maybe a
def Maybe (a -> String)
_) OptProperties -> OptProperties
g OptReader a
rdr =
  let
    o :: Parser a
o = Option a -> Parser a
forall a. Option a -> Parser a
liftOpt (Option a -> Parser a) -> Option a -> Parser a
forall a b. (a -> b) -> a -> b
$ DefaultProp a
-> (OptProperties -> OptProperties) -> OptReader a -> Option a
forall a.
DefaultProp a
-> (OptProperties -> OptProperties) -> OptReader a -> Option a
mkOption DefaultProp a
d OptProperties -> OptProperties
g OptReader a
rdr
  in
    Parser a -> (a -> Parser a) -> Maybe a -> Parser a
forall b a. b -> (a -> b) -> Maybe a -> b
maybe Parser a
o (\a
a -> Parser a
o Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure a
a) Maybe a
def

mkOption :: DefaultProp a
         -> (OptProperties -> OptProperties)
         -> OptReader a
         -> Option a
mkOption :: DefaultProp a
-> (OptProperties -> OptProperties) -> OptReader a -> Option a
mkOption DefaultProp a
d OptProperties -> OptProperties
g OptReader a
rdr = OptReader a -> OptProperties -> Option a
forall a. OptReader a -> OptProperties -> Option a
Option OptReader a
rdr (DefaultProp a -> (OptProperties -> OptProperties) -> OptProperties
forall a.
DefaultProp a -> (OptProperties -> OptProperties) -> OptProperties
mkProps DefaultProp a
d OptProperties -> OptProperties
g)

mkProps :: DefaultProp a
        -> (OptProperties -> OptProperties)
        -> OptProperties
mkProps :: DefaultProp a -> (OptProperties -> OptProperties) -> OptProperties
mkProps (DefaultProp Maybe a
def Maybe (a -> String)
sdef) OptProperties -> OptProperties
g = OptProperties
props
  where
    props :: OptProperties
props = (OptProperties -> OptProperties
g OptProperties
baseProps)
      { propShowDefault :: Maybe String
propShowDefault = Maybe (a -> String)
sdef Maybe (a -> String) -> Maybe a -> Maybe String
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Maybe a
def }

-- | Hide this option completely from the help text
--
-- Use 'hidden' if the option should remain visible in the full description.
internal :: Mod f a
internal :: Mod f a
internal = (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a. (OptProperties -> OptProperties) -> Mod f a
optionMod ((OptProperties -> OptProperties) -> Mod f a)
-> (OptProperties -> OptProperties) -> Mod f a
forall a b. (a -> b) -> a -> b
$ \OptProperties
p -> OptProperties
p { propVisibility :: OptVisibility
propVisibility = OptVisibility
Internal }


-- | Suppress this option from appearing in global options
noGlobal :: Mod f a
noGlobal :: Mod f a
noGlobal = (OptProperties -> OptProperties) -> Mod f a
forall (f :: * -> *) a. (OptProperties -> OptProperties) -> Mod f a
optionMod ((OptProperties -> OptProperties) -> Mod f a)
-> (OptProperties -> OptProperties) -> Mod f a
forall a b. (a -> b) -> a -> b
$ \OptProperties
pp -> OptProperties
pp { propShowGlobal :: Bool
propShowGlobal = Bool
False }