packages feed

bench-show-0.3.2: app/Main.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}

#if __GLASGOW_HASKELL__ >= 800
{-# OPTIONS_GHC -Wno-deprecations #-}
#endif

-- |
-- Module      : Main
-- Copyright   : (c) 2019 Composewell Technologies
--               (c) 2019 Sanchayan Maity
-- License     : BSD3
-- Maintainer  : harendra.kumar@gmail.com
-- Stability   : experimental
-- Portability : GHC
--
module Main where

import Options.Applicative.Simple (addCommand, simpleOptions, simpleVersion)
import Paths_bench_show (version)

import Options.Applicative

#ifndef NO_CHARTS
import BenchShow
#else
import BenchShow.Internal.Common
import BenchShow.Internal.Report
#endif

-- The command line interface provides subcommands for each task.  There are
-- some common options that apply to all subcommands. Subcommands may have
-- their own specific options as well.

-------------------------------------------------------------------------------
-- Common config options
-------------------------------------------------------------------------------

pVerbose :: Parser Bool
pVerbose = switch $
       long "verbose"
    <> short 'v'
    <> help "Provide more details in the report output"

pOutputDir :: Parser FilePath
pOutputDir = strOption
  ( long "output-dir"
    <> metavar "DIR"
    <> help "Default is current directory" )

pTitle :: Parser String
pTitle = strOption
  ( long "title"
    <> short 't'
    <> metavar "STRING"
    <> help "Title for the report" )

pTitleAnnotation :: Parser TitleAnnotation
pTitleAnnotation = option auto $
       long "title-annotations"
    <> help ("*TitleField*|TitleEstimator|TitleDiff")

pPresentation :: Parser Presentation
pPresentation = option auto $
       long "presentation"
    <> help ("Solo|Fields|*Groups* <*Absolute*|Diff|PercentDiff|Multiples")

pEstimator :: Parser Estimator
pEstimator = option auto $
       long "estimator"
    <> help ("*Median*|Mean|Regression")

pThreshold :: Parser Word
pThreshold = option auto $
       long "threshold"
    <> metavar "PERCENT"
    <> help "Min % diff (default 3) to flag regression or improvement"

pDiffStrategy :: Parser DiffStrategy
pDiffStrategy = option auto $
       long "diff-strategy"
    <> help ("*SingleEstimator*|MinEstimator")

pOmitBaseline :: Parser Bool
pOmitBaseline = switch $
       long "omit-baseline"
    <> help "omit the baseline column in relative comparisons"

-------------------------------------------------------------------------------
-- Build a Config parser for common options
-------------------------------------------------------------------------------

-- Specify a default value for a Maybe inside a functor
fMaybe :: Functor f => a -> f (Maybe a) -> f a
fMaybe a = fmap (maybe a id)

-- | parse an optional field with a default value taken from defaultConfig
parseOptional :: (Config -> a) -> Parser a -> Parser a
parseOptional def parser = fMaybe (def defaultConfig) (optional parser)

pConfig :: Parser Config
pConfig = Config
    <$> parseOptional verbose pVerbose
    <*> optional pOutputDir
    <*> pure Nothing
    <*> optional pTitle
    <*> many pTitleAnnotation
    <*> parseOptional presentation pPresentation
    <*> parseOptional estimator pEstimator
    <*> parseOptional threshold pThreshold
    <*> parseOptional diffStrategy pDiffStrategy
    <*> parseOptional omitBaseline pOmitBaseline
    <*> pure (selectFields defaultConfig)
    <*> pure (fieldRanges defaultConfig)
    <*> pure (fieldTicks defaultConfig)
    <*> pure (classifyBenchmark defaultConfig)
    <*> pure (selectGroups defaultConfig)
    <*> pure (selectBenchmarks defaultConfig)

-------------------------------------------------------------------------------
-- "report" subcommand
-------------------------------------------------------------------------------

data ReportOpts = ReportOpts
    { reportInput :: FilePath
    , reportOutput :: Maybe FilePath
    }

pInputFile :: Parser FilePath
pInputFile = argument str (metavar "INPUT-FILE.CSV")

pOutputFile :: Parser FilePath
pOutputFile = strOption
  (long "output"
  <> short 'o'
  <> help "Output file")

pReportOpts :: Parser ReportOpts
pReportOpts = ReportOpts
    <$> pInputFile
    <*> optional pOutputFile

cmdReport :: ReportOpts -> Config -> IO ()
cmdReport ReportOpts{..} cfg = report reportInput reportOutput cfg

-------------------------------------------------------------------------------
-- "graph" subcommand
-------------------------------------------------------------------------------

#ifndef NO_CHARTS

data GraphOpts = GraphOpts
    { graphInput :: FilePath
    , graphOutput :: FilePath
    }

pOutputArg :: Parser FilePath
pOutputArg = argument str (metavar "OUTPUT-FILE-PREFIX")

pGraphOpts :: Parser GraphOpts
pGraphOpts = GraphOpts
    <$> pInputFile
    <*> pOutputArg

cmdGraph :: GraphOpts -> Config -> IO ()
cmdGraph GraphOpts{..} cfg = graph graphInput graphOutput cfg

#endif

-------------------------------------------------------------------------------
-- Build and run a subcommand parser
-------------------------------------------------------------------------------

cmdLineParser :: Parser Config -> IO ()
cmdLineParser p = do
    (cfg, handler) <- simpleOptions $(simpleVersion version)
        ("Generate reports and graphs from gauge or criterion output.\n" ++
        "Default values are shown as *DEFAULT*.")
        "" p cmds

    handler cfg

    where cmds = do
            addCommand "report"
                       "Generate a text report"
                       cmdReport
                       pReportOpts

#ifndef NO_CHARTS
            addCommand "graph"
                       "Generate a graphical report"
                       cmdGraph
                       pGraphOpts
#endif

main :: IO ()
main = cmdLineParser pConfig