stan-0.0.1.0: src/Stan/Inspection.hs
{- |
Copyright: (c) 2020 Kowainik
SPDX-License-Identifier: MPL-2.0
Maintainer: Kowainik <xrom.xkov@gmail.com>
__Inspection__ — check or test provided by Stan.
-}
module Stan.Inspection
( -- * Stan inspection type
Inspection (..)
, categoryL
, descriptionL
, solutionL
, severityL
, analysisL
-- * Inspection info
, InspectionAnalysis (..)
, InspectionsMap
-- * Sorting
, sortById
-- * Pretty print
, prettyShowInspection
, prettyShowInspectionShort
-- ** Markdown
, inspectionsMd
) where
import Relude.Extra.Lens (Lens', lens)
import Colourista (blue, bold, formatWith, green)
import Colourista.Short (b, i)
import Data.Aeson.Micro (ToJSON (..), object, (.=))
import Stan.Category (Category (..), prettyShowCategory)
import Stan.Core.Id (Id (..))
import Stan.Pattern.Ast (PatternAst)
import Stan.Severity (Severity, prettyShowSeverity)
import qualified Data.HashMap.Strict as HM
import qualified Data.Text as T
{- | Data type that represents a check/test, or how we call it
__inspection__ that is provided by the Stan tool.
-}
data Inspection = Inspection
{ inspectionId :: !(Id Inspection)
, inspectionName :: !Text
, inspectionDescription :: !Text
, inspectionSolution :: ![Text]
, inspectionCategory :: !(NonEmpty Category)
, inspectionSeverity :: !Severity
, inspectionAnalysis :: !InspectionAnalysis
} deriving stock (Show, Eq)
instance ToJSON Inspection where
toJSON Inspection{..} = object
[ "id" .= inspectionId
, "name" .= inspectionName
, "description" .= inspectionDescription
, "solution" .= inspectionSolution
, "category" .= toList inspectionCategory
, "severity" .= inspectionSeverity
]
descriptionL :: Lens' Inspection Text
descriptionL = lens
inspectionDescription
(\inspection new -> inspection { inspectionDescription = new })
solutionL :: Lens' Inspection [Text]
solutionL = lens
inspectionSolution
(\inspection new -> inspection { inspectionSolution = new })
categoryL :: Lens' Inspection (NonEmpty Category)
categoryL = lens
inspectionCategory
(\inspection new -> inspection { inspectionCategory = new })
severityL :: Lens' Inspection Severity
severityL = lens
inspectionSeverity
(\inspection new -> inspection { inspectionSeverity = new })
analysisL :: Lens' Inspection InspectionAnalysis
analysisL = lens
inspectionAnalysis
(\inspection new -> inspection { inspectionAnalysis = new })
{- | Type alias for the 'HashMap' that contains pairs of inspections 'Id's and
corresponding 'Inspection's.
-}
type InspectionsMap = HashMap (Id Inspection) Inspection
-- | Sort 'Inspection' by 'Id'
sortById :: InspectionsMap -> [Inspection]
sortById = sortWith inspectionId . HM.elems
{- | Data type that represents all possible types of @stan@
inspections in a uniformed way.
-}
data InspectionAnalysis
-- | Find the specific part of the Haskell AST (including specific functions).
= FindAst !PatternAst
-- | Find all operators without matching @infix[r|l]@
| Infix
-- | Check if the data type has lazy fields
| LazyField
-- | Usage of tuples with size >= 4
| BigTuples
-- | Pattern matching on @_@ for sum types.
| PatternMatchOn_
-- | Replace multiple comparison operations with 'compare'
| UseCompare
deriving stock (Show, Eq)
-- | Show 'Inspection' in a human-friendly format.
prettyShowInspection :: Inspection -> Text
prettyShowInspection Inspection{..} = unlines $
[ b "~~~STAN INSPECTION~~~"
, ""
, i " ✲ ID: " <> b (unId inspectionId)
, i " ✲ Name: " <> inspectionName
, i " ✲ Description: " <> inspectionDescription
, i " ✲ Severity: " <> prettyShowSeverity inspectionSeverity
, i " ✲ Category: " <> T.intercalate " " (map prettyShowCategory $ toList inspectionCategory)
, ""
, formatWith [green] "Possible solutions:"
] <> map (" - " <>) inspectionSolution
-- | Show the short view of a given 'Inspection'.
prettyShowInspectionShort :: Inspection -> Text
prettyShowInspectionShort Inspection{..} =
" ❋ "
<> formatWith [bold, blue] ("[" <> unId inspectionId <> "] ")
<> i inspectionName
{- | Create the MarkDown text for all inspections.
The generated MD has a ToC and separate sections for each inspection.
This is used to keep the Wiki page of the project up to date.
-}
inspectionsMd :: [Inspection] -> Text
inspectionsMd inss = intro <> toc <> unlines (map inspectionToMd inss)
where
intro :: Text
intro = "This document contains information about all inspections used in Stan to find observations in your projects. Below you can see more details about each inspection individually\n\n"
toc :: Text
toc = "## Table of all Inspections\n\n" <> unlines (map insLink inss) <> "\n"
insLink :: Inspection -> Text
insLink (unId . inspectionId -> ins)= " * [" <> ins <> "](#" <> ins <> ")"
inspectionToMd :: Inspection -> Text
inspectionToMd Inspection{..} = unlines $
[ "## " <> unId inspectionId
, ""
, "[[Back to the Table of all Inspections] ↑](#table-of-all-inspections)"
, ""
, "| Property | Value |"
, "|--|--|"
, "| ID | " <> unId inspectionId <> " |"
, "| Name | " <> inspectionName <> " |"
, "| Description | " <> inspectionDescription <> " |"
, "| Severity | " <> show inspectionSeverity <> " |"
, "| Category | " <> "#" <> T.intercalate " #" (map unCategory $ toList inspectionCategory) <> " |"
, ""
, "#### Possible solutions for " <> unId inspectionId
] <> map (" - " <>) inspectionSolution