packages feed

stacker-0.0.1.0: src/Options.hs

{-# 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 = customExecParser (prefs showHelpOnEmpty) optionsParserInfo

-- | Parser info (exported for help rendering)
optionsParserInfo :: ParserInfo Options
optionsParserInfo = info (optionsParser <**> helper)
  ( fullDesc
 <> progDesc "A tool to bump snapshots (resolvers) in stack*.yaml files"
 <> header ("stacker version " ++ appVersion)
 <> footer "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 = subcommandWithOptions <|> topLevelOptions
  where
    -- Helper to combine a command parser with the color option, creating an Options value
    withColorOption :: Parser Command -> Parser Options
    withColorOption cmdParser = Options <$> cmdParser <*> colorOption

    -- Subcommand parser that includes color options for each command
    subcommandWithOptions = subparser
      ( command "bump" (info (withColorOption bumpParser) (progDesc "Update stack*.yaml files (optionally specify files, or use --recursive/-r to search subdirectories)"))
     <> command "dry-run" (info (withColorOption dryRunParser) (progDesc "Show what would be updated (default, optionally specify files, or use --recursive/-r to search subdirectories)"))
     <> command "update" (info (withColorOption (pure Update)) (progDesc "Update stackage snapshots database"))
     <> command "info" (info (withColorOption infoCmdParser) (progDesc "Print repository path and GHC version to snapshot mapping"))
     <> command "config" (info (withColorOption configParser) (progDesc "Configure stacker"))
     <> command "version" (info (withColorOption (pure Version)) (progDesc "Print version information (also: -V, --version)"))
     <> command "numeric-version" (info (withColorOption (pure NumericVersion)) (progDesc "Print version number (also: --numeric-version)"))
     <> command "license" (info (withColorOption (pure PrintLicense)) (progDesc "Print license text (also: --license)"))
     <> command "help" (info (withColorOption (pure Help)) (progDesc "Print this help (also: -h, --help)"))
      )

    -- Top-level parser for version/help flags and default command when no subcommand is specified
    topLevelOptions = Options
      <$> commandParserTopLevel
      <*> colorOption

    -- Top-level command parser (for flags)
    commandParserTopLevel =
      flag' Version (long "version" <> short 'V' <> help "Print version information")
      <|> flag' NumericVersion (long "numeric-version" <> help "Print version number")
      <|> flag' PrintLicense (long "license" <> help "Print license text")
      <|> flag' Help (long "help" <> short 'h' <> help "Print help")
      <|> (DryRun [] <$> recursiveOption)

-- | Config command parser
configParser :: Parser Command
configParser = Config <$> subparser
  ( command "repo" (info repoParser (progDesc "Set repository path"))
  )

-- | Info command parser
infoCmdParser :: Parser Command
infoCmdParser = Info <$> (subparser
  ( command "repo" (info (pure InfoRepo) (progDesc "Print repository path"))
 <> command "snapshots" (info (pure InfoSnapshots) (progDesc "Print GHC version to snapshot mapping"))
  ) <|> pure InfoAll)

-- | Repo config parser
repoParser :: Parser ConfigCmd
repoParser = SetRepo <$> argument str (metavar "PATH")

-- | Helper to parse file arguments with optional recursive flag
filesParser :: ([FilePath] -> Bool -> Command) -> Parser Command
filesParser cmd = cmd
  <$> many (argument str (metavar "FILES..." <> action "file"))
  <*> recursiveOption

-- | Recursive option parser
recursiveOption :: Parser Bool
recursiveOption = switch
  ( long "recursive"
 <> short 'r'
 <> help "Search for stack*.yaml files recursively in all subdirectories" )

-- | Bump command parser
bumpParser :: Parser Command
bumpParser = filesParser Bump

-- | Dry-run command parser
dryRunParser :: Parser Command
dryRunParser = filesParser DryRun

-- | Color option parser
colorOption :: Parser ColorWhen
colorOption = option readColorWhen
  ( long "color"
 <> metavar "WHEN"
 <> value Auto
 <> help "Use colored output (always, never, auto)" )
  where
    readColorWhen = maybeReader $ \s ->
      case map toLower s of
        "always" -> Just Always
        "never" -> Just Never
        "auto" -> Just Auto
        _ -> Nothing