{-# OPTIONS_HADDOCK hide #-}
-- |
-- Module:     Trace.Hpc.Codecov.Options
-- Copyright:  (c) 2020 8c6794b6
-- License:    BSD3
-- Maintainer: 8c6794b6 <8c6794b6@gmail.com>
--
-- Command line options for generating Codecov test coverage report.

module Trace.Hpc.Codecov.Options
  (
    -- * The Options type and predefined values
    Options(..)
  , defaultOptions
  , emptyOptions

    -- * Command line parser for 'Options'
  , parseOptions

    -- * Converter
  , opt2rpt

    -- * Help message and version number
  , printHelp
  , printVersion
  , versionString
  ) where

-- base
import Control.Exception        (throw)
import Data.Version             (showVersion)
import System.Console.GetOpt    (ArgDescr (..), ArgOrder (..),
                                 OptDescr (..), getOpt, usageInfo)
import System.Environment       (getProgName)

-- Internal
import Paths_hpc_codecov        (version)
import Trace.Hpc.Codecov.Error
import Trace.Hpc.Codecov.Report

-- | Options for generating test coverage report.
data Options = Options
  { Options -> FilePath
optTix         :: FilePath
    -- ^ Input tix file.
  , Options -> [FilePath]
optMixDirs     :: [FilePath]
    -- ^ Directory containing mix files referred by the tix file.
  , Options -> [FilePath]
optSrcDirs     :: [FilePath]
    -- ^ Directory containing source codes referred by the mix files.
  , Options -> [FilePath]
optExcludes    :: [String]
    -- ^ Module name strings to exclude from coverage report.
  , Options -> Maybe FilePath
optOutFile     :: Maybe FilePath
    -- ^ Output file to write JSON report, if given.
  , Options -> Bool
optVerbose     :: Bool
    -- ^ Flag for showing verbose message during coverage report
    -- generation.
  , Options -> Bool
optShowVersion :: Bool
    -- ^ Flag for showing version.
  , Options -> Bool
optShowNumeric :: Bool
    -- ^ Flag for showing numeric version.
  , Options -> Bool
optShowHelp    :: Bool
    -- ^ Flag for showing help message.
  } deriving (Options -> Options -> Bool
(Options -> Options -> Bool)
-> (Options -> Options -> Bool) -> Eq Options
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Options -> Options -> Bool
$c/= :: Options -> Options -> Bool
== :: Options -> Options -> Bool
$c== :: Options -> Options -> Bool
Eq, Int -> Options -> ShowS
[Options] -> ShowS
Options -> FilePath
(Int -> Options -> ShowS)
-> (Options -> FilePath) -> ([Options] -> ShowS) -> Show Options
forall a.
(Int -> a -> ShowS) -> (a -> FilePath) -> ([a] -> ShowS) -> Show a
showList :: [Options] -> ShowS
$cshowList :: [Options] -> ShowS
show :: Options -> FilePath
$cshow :: Options -> FilePath
showsPrec :: Int -> Options -> ShowS
$cshowsPrec :: Int -> Options -> ShowS
Show)

-- | Empty 'Options'.
emptyOptions :: Options
emptyOptions :: Options
emptyOptions = Options :: FilePath
-> [FilePath]
-> [FilePath]
-> [FilePath]
-> Maybe FilePath
-> Bool
-> Bool
-> Bool
-> Bool
-> Options
Options
  { optTix :: FilePath
optTix = HpcCodecovError -> FilePath
forall a e. Exception e => e -> a
throw HpcCodecovError
NoTixFile
  , optMixDirs :: [FilePath]
optMixDirs = []
  , optSrcDirs :: [FilePath]
optSrcDirs = []
  , optExcludes :: [FilePath]
optExcludes = []
  , optOutFile :: Maybe FilePath
optOutFile = Maybe FilePath
forall a. Maybe a
Nothing
  , optVerbose :: Bool
optVerbose = Bool
False
  , optShowVersion :: Bool
optShowVersion = Bool
False
  , optShowNumeric :: Bool
optShowNumeric = Bool
False
  , optShowHelp :: Bool
optShowHelp = Bool
False
  }

-- | The default 'Options'.
defaultOptions :: Options
defaultOptions :: Options
defaultOptions = Options
emptyOptions
  { optMixDirs :: [FilePath]
optMixDirs = [FilePath
".hpc"]
  , optSrcDirs :: [FilePath]
optSrcDirs = [FilePath
""]
  }

-- | Commandline option oracle.
options :: [OptDescr (Options -> Options)]
options :: [OptDescr (Options -> Options)]
options =
  [ FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
'm'] [FilePath
"mixdir"]
            ((FilePath -> Options -> Options)
-> FilePath -> ArgDescr (Options -> Options)
forall a. (FilePath -> a) -> FilePath -> ArgDescr a
ReqArg (\FilePath
d Options
o -> Options
o {optMixDirs :: [FilePath]
optMixDirs = FilePath
d FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: Options -> [FilePath]
optMixDirs Options
o})
                    FilePath
"DIR")
            FilePath
".mix file directory, can repeat\n\
            \default is .hpc"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
's'] [FilePath
"srcdir"]
           ((FilePath -> Options -> Options)
-> FilePath -> ArgDescr (Options -> Options)
forall a. (FilePath -> a) -> FilePath -> ArgDescr a
ReqArg (\FilePath
d Options
o -> Options
o {optSrcDirs :: [FilePath]
optSrcDirs = FilePath
d FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: Options -> [FilePath]
optSrcDirs Options
o})
                   FilePath
"DIR")
           FilePath
"source directory, can repeat\n\
           \default is current directory"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
'x'] [FilePath
"exclude"]
           ((FilePath -> Options -> Options)
-> FilePath -> ArgDescr (Options -> Options)
forall a. (FilePath -> a) -> FilePath -> ArgDescr a
ReqArg (\FilePath
m Options
o -> Options
o {optExcludes :: [FilePath]
optExcludes = FilePath
m FilePath -> [FilePath] -> [FilePath]
forall a. a -> [a] -> [a]
: Options -> [FilePath]
optExcludes Options
o})
                   FilePath
"MODULE")
           FilePath
"module name to exclude, can repeat"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
'o'] [FilePath
"out"]
           ((FilePath -> Options -> Options)
-> FilePath -> ArgDescr (Options -> Options)
forall a. (FilePath -> a) -> FilePath -> ArgDescr a
ReqArg (\FilePath
p Options
o -> Options
o {optOutFile :: Maybe FilePath
optOutFile = FilePath -> Maybe FilePath
forall a. a -> Maybe a
Just FilePath
p}) FilePath
"FILE")
           FilePath
"output file\n\
           \default is stdout"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
'v'] [FilePath
"verbose"]
           ((Options -> Options) -> ArgDescr (Options -> Options)
forall a. a -> ArgDescr a
NoArg (\Options
o -> Options
o {optVerbose :: Bool
optVerbose = Bool
True}))
           FilePath
"show verbose output"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [] [FilePath
"version"]
           ((Options -> Options) -> ArgDescr (Options -> Options)
forall a. a -> ArgDescr a
NoArg (\Options
o -> Options
o {optShowVersion :: Bool
optShowVersion = Bool
True}))
           FilePath
"show versoin and exit"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [] [FilePath
"numeric-version"]
           ((Options -> Options) -> ArgDescr (Options -> Options)
forall a. a -> ArgDescr a
NoArg (\Options
o -> Options
o {optShowNumeric :: Bool
optShowNumeric = Bool
True}))
           FilePath
"show numeric version and exit"
  , FilePath
-> [FilePath]
-> ArgDescr (Options -> Options)
-> FilePath
-> OptDescr (Options -> Options)
forall a.
FilePath -> [FilePath] -> ArgDescr a -> FilePath -> OptDescr a
Option [Char
'h'] [FilePath
"help"]
           ((Options -> Options) -> ArgDescr (Options -> Options)
forall a. a -> ArgDescr a
NoArg (\Options
o -> Options
o {optShowHelp :: Bool
optShowHelp = Bool
True}))
           FilePath
"show this help"
  ]

-- | Parse command line argument and return either error messages or
-- parsed 'Options'.
parseOptions :: [String] -- ^ Command line argument strings.
             -> Either [String] Options
parseOptions :: [FilePath] -> Either [FilePath] Options
parseOptions [FilePath]
args =
  case ArgOrder (Options -> Options)
-> [OptDescr (Options -> Options)]
-> [FilePath]
-> ([Options -> Options], [FilePath], [FilePath])
forall a.
ArgOrder a
-> [OptDescr a] -> [FilePath] -> ([a], [FilePath], [FilePath])
getOpt ArgOrder (Options -> Options)
forall a. ArgOrder a
Permute [OptDescr (Options -> Options)]
options [FilePath]
args of
    ([Options -> Options]
flags, [FilePath]
rest, []) ->
      -- Not returning error messages with missing ".tix" file
      -- argument at this point, to show help and version messages
      -- without specifying ".tix" file.
      let opts0 :: Options
opts0 = ((Options -> Options)
 -> (Options -> Options) -> Options -> Options)
-> (Options -> Options)
-> [Options -> Options]
-> Options
-> Options
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (Options -> Options) -> (Options -> Options) -> Options -> Options
forall b c a. (b -> c) -> (a -> b) -> a -> c
(.) Options -> Options
forall a. a -> a
id [Options -> Options]
flags (Options -> Options) -> Options -> Options
forall a b. (a -> b) -> a -> b
$ Options
emptyOptions
          opts1 :: Options
opts1 = Options -> Options
fillDefaultIfNotGiven Options
opts0
      in  case [FilePath]
rest of
            []      -> Options -> Either [FilePath] Options
forall a b. b -> Either a b
Right Options
opts1
            (FilePath
tix:[FilePath]
_) -> Options -> Either [FilePath] Options
forall a b. b -> Either a b
Right (Options
opts1 {optTix :: FilePath
optTix = FilePath
tix})
    ([Options -> Options]
_, [FilePath]
_, [FilePath]
errs)  -> [FilePath] -> Either [FilePath] Options
forall a b. a -> Either a b
Left [FilePath]
errs

fillDefaultIfNotGiven :: Options -> Options
fillDefaultIfNotGiven :: Options -> Options
fillDefaultIfNotGiven Options
opts = Options
opts
  { optMixDirs :: [FilePath]
optMixDirs = ([FilePath] -> Bool) -> (Options -> [FilePath]) -> [FilePath]
forall p. (p -> Bool) -> (Options -> p) -> p
fillIf [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null Options -> [FilePath]
optMixDirs
  , optSrcDirs :: [FilePath]
optSrcDirs = ([FilePath] -> Bool) -> (Options -> [FilePath]) -> [FilePath]
forall p. (p -> Bool) -> (Options -> p) -> p
fillIf [FilePath] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null Options -> [FilePath]
optSrcDirs
  }
 where
   fillIf :: (p -> Bool) -> (Options -> p) -> p
fillIf p -> Bool
test Options -> p
fld =
      let orig :: p
orig = Options -> p
fld Options
opts
      in  if p -> Bool
test p
orig
             then Options -> p
fld Options
defaultOptions
             else p
orig

-- | Make a 'Report' value from 'Optoins'.
opt2rpt :: Options -> Report
opt2rpt :: Options -> Report
opt2rpt Options
opt = Report :: FilePath
-> [FilePath]
-> [FilePath]
-> [FilePath]
-> Maybe FilePath
-> Bool
-> Report
Report
  { reportTix :: FilePath
reportTix = Options -> FilePath
optTix Options
opt
  , reportMixDirs :: [FilePath]
reportMixDirs = Options -> [FilePath]
optMixDirs Options
opt
  , reportSrcDirs :: [FilePath]
reportSrcDirs = Options -> [FilePath]
optSrcDirs Options
opt
  , reportExcludes :: [FilePath]
reportExcludes = Options -> [FilePath]
optExcludes Options
opt
  , reportOutFile :: Maybe FilePath
reportOutFile = Options -> Maybe FilePath
optOutFile Options
opt
  , reportVerbose :: Bool
reportVerbose = Options -> Bool
optVerbose Options
opt
  }

-- | Print help messages.
printHelp :: IO ()
printHelp :: IO ()
printHelp = IO FilePath
getProgName IO FilePath -> (FilePath -> IO ()) -> IO ()
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= FilePath -> IO ()
putStrLn (FilePath -> IO ()) -> ShowS -> FilePath -> IO ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ShowS
helpMessage

-- | Print version number of this package.
printVersion :: IO ()
printVersion :: IO ()
printVersion =
  do FilePath
me <- IO FilePath
getProgName
     FilePath -> IO ()
putStrLn (FilePath
me FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
" version " FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
versionString)

-- | Help message for command line output.
helpMessage :: String -- ^ Executable program name.
            -> String
helpMessage :: ShowS
helpMessage FilePath
name = FilePath -> [OptDescr (Options -> Options)] -> FilePath
forall a. FilePath -> [OptDescr a] -> FilePath
usageInfo FilePath
header [OptDescr (Options -> Options)]
options
  where
    header :: FilePath
header = FilePath
"\
\Generate Codecov JSON coverage report from .tix and .mix files\n\
\\n\
\Usage: \n\
\\n\
\   " FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
name FilePath -> ShowS
forall a. [a] -> [a] -> [a]
++ FilePath
" [OPTIONS] TIX_FILE\n\
\\n\
\Options:\n"

-- | String representation of the version number of this package.
versionString :: String
versionString :: FilePath
versionString = Version -> FilePath
showVersion Version
version