module Lib where
import Text.Regex.PCRE.Heavy
import Data.ByteString (ByteString)
import Data.String.Interpolate
-- | Data structure for a single threshold configuration.
data Threshold = Threshold
{ thresholdName :: String
, thresholdRegex :: ByteString
, thresholdValue :: Double
} deriving (Read, Show, Eq)
type Coverage = Double
-- | The result of evaluation. The _3 indicates whether the coverage passes the threshold or not.
type ThresholdEvaluationResult = (Threshold, Coverage, Bool)
-- | Extract the coverage from input string using a regex string
extractCoverage
:: String -- ^ The input string
-> ByteString -- ^ The regex to extract the coverage. The regex should contain `(\\d+)` capture otherwise the coverage is always be 0.
-> Coverage -- ^ The extracted coverage
extractCoverage src regexStr =
case compileM regexStr [] of
Left e ->
error $ "Unable to compile regex " ++ show regexStr ++ ": " ++ e
Right regex ->
case scan regex src of
(_, coverage:_):_ ->
read coverage
_ ->
0
-- | Evaluate the given string against the given threshold, producing an evaluation result
evaluate :: String -> Threshold -> ThresholdEvaluationResult
evaluate src threshold =
let
coverage =
extractCoverage src (thresholdRegex threshold)
isPass =
coverage >= thresholdValue threshold
in
(threshold, coverage, isPass)
-- | Produce a human-friendly output of the evaluation result
reportThreshold :: ThresholdEvaluationResult -> String
reportThreshold (Threshold tName _ tValue, coverage, isPass) =
let
remark = if isPass then "✓" else "·" :: String
sign = if isPass then "≥" else "<" :: String
in
[i|#{remark} #{tName}: #{coverage}% (#{sign} #{tValue}%)|]
-- | Evaluate a string against a list of thresholds configuration and produce a report
evaluateAndReport
:: String -- ^ The input string
-> [Threshold] -- ^ The list of thresholds
-> (String, Bool) -- ^ Pair of report and whether there is any coverage under thresholds
evaluateAndReport src thresholds =
let
evalResults = map (evaluate src) thresholds
isAllThresholdPass = all (\(_, _, isPass) -> isPass) evalResults
reportSummary =
if isAllThresholdPass
then "Code coverage threshold check: PASS"
else "Code coverage threshold check: FAIL"
report = unlines $ reportSummary : map reportThreshold evalResults
in
(report, isAllThresholdPass)