{-# LANGUAGE OverloadedStrings #-}

module Options
  ( parseOptions
  , optionsParserInfo
  , Options(..)
  ) where

import Data.Char (toLower)
import Options.Applicative
import Types

-- | Parse command-line options
parseOptions :: IO Options
parseOptions :: IO Options
parseOptions = ParserPrefs -> ParserInfo Options -> IO Options
forall a. ParserPrefs -> ParserInfo a -> IO a
customExecParser (PrefsMod -> ParserPrefs
prefs PrefsMod
showHelpOnEmpty) ParserInfo Options
optionsParserInfo

-- | Parser info (exported for help rendering)
optionsParserInfo :: ParserInfo Options
optionsParserInfo :: ParserInfo Options
optionsParserInfo = Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Options
optionsParser Parser Options -> Parser (Options -> Options) -> Parser Options
forall (f :: * -> *) a b. Applicative f => f a -> f (a -> b) -> f b
<**> Parser (Options -> Options)
forall a. Parser (a -> a)
helper)
  ( InfoMod Options
forall a. InfoMod a
fullDesc
 InfoMod Options -> InfoMod Options -> InfoMod Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"A tool to bump snapshots (resolvers) in stack*.yaml files"
 InfoMod Options -> InfoMod Options -> InfoMod Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
header (FilePath
"stacker version " FilePath -> FilePath -> FilePath
forall a. [a] -> [a] -> [a]
++ FilePath
appVersion)
 InfoMod Options -> InfoMod Options -> InfoMod Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
footer FilePath
"For more information, see the README" )

-- | Options parser
-- The parser tries subcommand parsing first (which includes color options),
-- then falls back to top-level options (for version/help flags and default).
-- This allows --color to be specified either before the subcommand
-- (e.g., "stacker --color=never dry-run") or within the subcommand
-- (e.g., "stacker dry-run --color=never file.yaml").
-- Precedence: Subcommand parsers take priority, so "stacker dry-run --color=never"
-- will be parsed by the subcommand parser, not the top-level parser.
optionsParser :: Parser Options
optionsParser :: Parser Options
optionsParser = Parser Options
subcommandWithOptions Parser Options -> Parser Options -> Parser Options
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Parser Options
topLevelOptions
  where
    -- Helper to combine a command parser with the color option, creating an Options value
    withColorOption :: Parser Command -> Parser Options
    withColorOption :: Parser Command -> Parser Options
withColorOption Parser Command
cmdParser = Command -> ColorWhen -> Options
Options (Command -> ColorWhen -> Options)
-> Parser Command -> Parser (ColorWhen -> Options)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Command
cmdParser Parser (ColorWhen -> Options) -> Parser ColorWhen -> Parser Options
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser ColorWhen
colorOption
    
    -- Subcommand parser that includes color options for each command
    subcommandWithOptions :: Parser Options
subcommandWithOptions = Mod CommandFields Options -> Parser Options
forall a. Mod CommandFields a -> Parser a
subparser
      ( FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"bump" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption Parser Command
bumpParser) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Update stack*.yaml files (optionally specify files)"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"dry-run" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption Parser Command
dryRunParser) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Show what would be updated (default, optionally specify files)"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"update" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
Update)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Update stackage snapshots database"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"info" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
Info)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Print GHC version to snapshot mapping"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"config" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption Parser Command
configParser) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Configure stacker"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"version" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
Version)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Print version information (also: -V, --version)"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"numeric-version" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
NumericVersion)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Print version number (also: --numeric-version)"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"license" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
PrintLicense)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Print license text (also: --license)"))
     Mod CommandFields Options
-> Mod CommandFields Options -> Mod CommandFields Options
forall a. Semigroup a => a -> a -> a
<> FilePath -> ParserInfo Options -> Mod CommandFields Options
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"help" (Parser Options -> InfoMod Options -> ParserInfo Options
forall a. Parser a -> InfoMod a -> ParserInfo a
info (Parser Command -> Parser Options
withColorOption (Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Command
Help)) (FilePath -> InfoMod Options
forall a. FilePath -> InfoMod a
progDesc FilePath
"Print this help (also: -h, --help)"))
      )
    
    -- Top-level parser for version/help flags and default command when no subcommand is specified
    topLevelOptions :: Parser Options
topLevelOptions = Command -> ColorWhen -> Options
Options
      (Command -> ColorWhen -> Options)
-> Parser Command -> Parser (ColorWhen -> Options)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser Command
commandParserTopLevel
      Parser (ColorWhen -> Options) -> Parser ColorWhen -> Parser Options
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Parser ColorWhen
colorOption
    
    -- Top-level command parser (for flags)
    commandParserTopLevel :: Parser Command
commandParserTopLevel = 
      Command -> Mod FlagFields Command -> Parser Command
forall a. a -> Mod FlagFields a -> Parser a
flag' Command
Version (FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"version" Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'V' Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Print version information")
      Parser Command -> Parser Command -> Parser Command
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Command -> Mod FlagFields Command -> Parser Command
forall a. a -> Mod FlagFields a -> Parser a
flag' Command
NumericVersion (FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"numeric-version" Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Print version number")
      Parser Command -> Parser Command -> Parser Command
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Command -> Mod FlagFields Command -> Parser Command
forall a. a -> Mod FlagFields a -> Parser a
flag' Command
PrintLicense (FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"license" Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Print license text")
      Parser Command -> Parser Command -> Parser Command
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Command -> Mod FlagFields Command -> Parser Command
forall a. a -> Mod FlagFields a -> Parser a
flag' Command
Help (FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"help" Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> Char -> Mod FlagFields Command
forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'h' Mod FlagFields Command
-> Mod FlagFields Command -> Mod FlagFields Command
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod FlagFields Command
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Print help")
      Parser Command -> Parser Command -> Parser Command
forall a. Parser a -> Parser a -> Parser a
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> Command -> Parser Command
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure ([FilePath] -> Command
DryRun [])

-- | Config command parser
configParser :: Parser Command
configParser :: Parser Command
configParser = ConfigCmd -> Command
Config (ConfigCmd -> Command) -> Parser ConfigCmd -> Parser Command
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Mod CommandFields ConfigCmd -> Parser ConfigCmd
forall a. Mod CommandFields a -> Parser a
subparser
  ( FilePath -> ParserInfo ConfigCmd -> Mod CommandFields ConfigCmd
forall a. FilePath -> ParserInfo a -> Mod CommandFields a
command FilePath
"repo" (Parser ConfigCmd -> InfoMod ConfigCmd -> ParserInfo ConfigCmd
forall a. Parser a -> InfoMod a -> ParserInfo a
info Parser ConfigCmd
repoParser (FilePath -> InfoMod ConfigCmd
forall a. FilePath -> InfoMod a
progDesc FilePath
"Set repository path"))
  )

-- | Repo config parser
repoParser :: Parser ConfigCmd
repoParser :: Parser ConfigCmd
repoParser = FilePath -> ConfigCmd
SetRepo (FilePath -> ConfigCmd) -> Parser FilePath -> Parser ConfigCmd
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ReadM FilePath -> Mod ArgumentFields FilePath -> Parser FilePath
forall a. ReadM a -> Mod ArgumentFields a -> Parser a
argument ReadM FilePath
forall s. IsString s => ReadM s
str (FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"PATH")

-- | Helper to parse file arguments
filesParser :: ([FilePath] -> Command) -> Parser Command
filesParser :: ([FilePath] -> Command) -> Parser Command
filesParser [FilePath] -> Command
cmd = [FilePath] -> Command
cmd ([FilePath] -> Command) -> Parser [FilePath] -> Parser Command
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Parser FilePath -> Parser [FilePath]
forall a. Parser a -> Parser [a]
forall (f :: * -> *) a. Alternative f => f a -> f [a]
many (ReadM FilePath -> Mod ArgumentFields FilePath -> Parser FilePath
forall a. ReadM a -> Mod ArgumentFields a -> Parser a
argument ReadM FilePath
forall s. IsString s => ReadM s
str (FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"FILES..." Mod ArgumentFields FilePath
-> Mod ArgumentFields FilePath -> Mod ArgumentFields FilePath
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod ArgumentFields FilePath
forall (f :: * -> *) a. HasCompleter f => FilePath -> Mod f a
action FilePath
"file"))

-- | Bump command parser
bumpParser :: Parser Command
bumpParser :: Parser Command
bumpParser = ([FilePath] -> Command) -> Parser Command
filesParser [FilePath] -> Command
Bump

-- | Dry-run command parser
dryRunParser :: Parser Command
dryRunParser :: Parser Command
dryRunParser = ([FilePath] -> Command) -> Parser Command
filesParser [FilePath] -> Command
DryRun

-- | Color option parser
colorOption :: Parser ColorWhen
colorOption :: Parser ColorWhen
colorOption = ReadM ColorWhen -> Mod OptionFields ColorWhen -> Parser ColorWhen
forall a. ReadM a -> Mod OptionFields a -> Parser a
option ReadM ColorWhen
readColorWhen
  ( FilePath -> Mod OptionFields ColorWhen
forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"color"
 Mod OptionFields ColorWhen
-> Mod OptionFields ColorWhen -> Mod OptionFields ColorWhen
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields ColorWhen
forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"WHEN"
 Mod OptionFields ColorWhen
-> Mod OptionFields ColorWhen -> Mod OptionFields ColorWhen
forall a. Semigroup a => a -> a -> a
<> ColorWhen -> Mod OptionFields ColorWhen
forall (f :: * -> *) a. HasValue f => a -> Mod f a
value ColorWhen
Auto
 Mod OptionFields ColorWhen
-> Mod OptionFields ColorWhen -> Mod OptionFields ColorWhen
forall a. Semigroup a => a -> a -> a
<> FilePath -> Mod OptionFields ColorWhen
forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Use colored output (always, never, auto)" )
  where
    readColorWhen :: ReadM ColorWhen
readColorWhen = (FilePath -> Maybe ColorWhen) -> ReadM ColorWhen
forall a. (FilePath -> Maybe a) -> ReadM a
maybeReader ((FilePath -> Maybe ColorWhen) -> ReadM ColorWhen)
-> (FilePath -> Maybe ColorWhen) -> ReadM ColorWhen
forall a b. (a -> b) -> a -> b
$ \FilePath
s ->
      case (Char -> Char) -> FilePath -> FilePath
forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower FilePath
s of
        FilePath
"always" -> ColorWhen -> Maybe ColorWhen
forall a. a -> Maybe a
Just ColorWhen
Always
        FilePath
"never" -> ColorWhen -> Maybe ColorWhen
forall a. a -> Maybe a
Just ColorWhen
Never
        FilePath
"auto" -> ColorWhen -> Maybe ColorWhen
forall a. a -> Maybe a
Just ColorWhen
Auto
        FilePath
_ -> Maybe ColorWhen
forall a. Maybe a
Nothing