wrecker-0.1.1.0: src/Wrecker/Options.hs
{-# LANGUAGE RecordWildCards, CPP #-}
module Wrecker.Options where
import Options.Applicative.Builder
import Options.Applicative
import Control.Exception
import Wrecker.Logger
import Data.Monoid
data RunType = RunCount Int | RunTimed Int
deriving (Show, Eq)
data DisplayMode = Interactive | NonInteractive
deriving (Show, Eq, Read)
data Options = Options
{ concurrency :: Int
-- ^ The number of simulatanous connections
, binCount :: Int
-- ^ The number of bins for the histogram
, runStyle :: RunType
-- ^ runStyle determines if the 'wrecker' runs for a specified
-- time period or for a specified number of runs.
, timeoutTime :: Int
-- ^ How long to wait after the first benchmark for the other threads
-- to finish
, displayMode :: DisplayMode
-- ^ This controls the command line display. It can be either Interactive
-- of NonInteractive
, logLevel :: LogLevel
-- ^
, match :: String
-- ^ Set this to filter the benchmarks using a pattern
, requestNameColumnSize :: Maybe Int
-- ^ Limit the request name column to the given size
, outputFilePath :: Maybe FilePath
-- ^ Dump the results to JSON file
, silent :: Bool
-- ^ Set 'silent' to true to disable all output.
} deriving (Show, Eq)
-- | 'defaultOptions' provides sensible default for the 'Options'
-- types
defaultOptions :: Options
defaultOptions = Options
{ concurrency = 1
, binCount = 20
, runStyle = RunCount 1
, timeoutTime = 100000000
, displayMode = NonInteractive
, logLevel = LevelError
, match = ""
, requestNameColumnSize = Nothing
, outputFilePath = Nothing
, silent = False
}
data PartialOptions = PartialOptions
{ mConcurrency :: Maybe Int
, mBinCount :: Maybe Int
, mRunStyle :: Maybe RunType
, mTimeoutTime :: Maybe Int
, mDisplayMode :: Maybe DisplayMode
, mLogLevel :: Maybe LogLevel
, mMatch :: Maybe String
, mRequestNameColumnSize :: Maybe Int
, mOutputFilePath :: Maybe FilePath
, mSilent :: Maybe Bool
} deriving (Show, Eq)
instance Monoid PartialOptions where
mempty = PartialOptions
{ mConcurrency = Just $ concurrency defaultOptions
, mBinCount = Just $ binCount defaultOptions
, mRunStyle = Just $ runStyle defaultOptions
, mTimeoutTime = Just $ timeoutTime defaultOptions
, mDisplayMode = Just $ displayMode defaultOptions
, mLogLevel = Just $ logLevel defaultOptions
, mMatch = Just $ match defaultOptions
, mRequestNameColumnSize = requestNameColumnSize defaultOptions
, mOutputFilePath = outputFilePath defaultOptions
, mSilent = Just $ silent defaultOptions
}
mappend x y = PartialOptions
{ mConcurrency = mConcurrency x <|> mConcurrency y
, mBinCount = mBinCount x <|> mBinCount y
, mRunStyle = mRunStyle x <|> mRunStyle y
, mTimeoutTime = mTimeoutTime x <|> mTimeoutTime y
, mDisplayMode = mDisplayMode x <|> mDisplayMode y
, mLogLevel = mLogLevel x <|> mLogLevel y
, mMatch = mMatch x <|> mMatch y
, mRequestNameColumnSize = mRequestNameColumnSize x
<|> mRequestNameColumnSize y
, mOutputFilePath = mOutputFilePath x
<|> mOutputFilePath y
, mSilent = mSilent x <|> mSilent y
}
completeOptions :: PartialOptions -> Maybe Options
completeOptions options =
case options <> mempty of
PartialOptions
{ mConcurrency = Just concurrency
, mBinCount = Just binCount
, mRunStyle = Just runStyle
, mTimeoutTime = Just timeoutTime
, mDisplayMode = Just displayMode
, mLogLevel = Just logLevel
, mMatch = Just match
, mRequestNameColumnSize = requestNameColumnSize
, mOutputFilePath = outputFilePath
, mSilent = Just silent
} -> Just $ Options {..}
_ -> Nothing
optionalOption :: Read a => Mod OptionFields a -> Parser (Maybe a)
optionalOption = optional . option auto
optionalStrOption :: Mod OptionFields String -> Parser (Maybe String)
optionalStrOption = optional . strOption
optionalSwitch :: Mod FlagFields Bool -> Parser (Maybe Bool)
optionalSwitch = optional . switch
pPartialOptions :: Parser PartialOptions
pPartialOptions
= PartialOptions
<$> optionalOption
( long "concurrency"
<> help "Number of threads for concurrent requests"
)
<*> optionalOption
( long "bin-count"
<> help "Number of bins for latency histogram"
)
<*> optional
( RunCount <$> option auto
( long "run-count"
<> help "number of times to repeat "
)
<|> RunTimed <$> option auto
( long "run-timed"
<> help "number of seconds to repeat "
)
)
<*> optionalOption
( long "timeout-time"
<> help "How long to wait for all requests to finish"
)
<*> optionalOption
( long "display-mode"
<> help "Display results interactively"
)
<*> optionalOption
( long "log-level"
<> help "Log to stderr events of criticality greater than the LOG_LEVEL"
)
<*> optionalStrOption
( long "match"
<> help "Only run tests that match the glob"
)
<*> optionalOption
( long "request-name-size"
<> help "Request name size for the terminal display"
)
<*> optionalStrOption
( long "output-path"
<> help "Save a JSON file of the the statistics to given path"
)
<*> optionalSwitch
( long "silent"
<> help "Disable all output"
)
runParser :: IO Options
runParser = do
let opts = info (helper <*> pPartialOptions)
( fullDesc
<> progDesc "Welcome to wrecker"
<> header "wrecker - HTTP stress tester and benchmarker"
)
partialOptions <- execParser opts
case completeOptions partialOptions of
Nothing -> throwIO $ userError ""
Just x -> return x