common/util/Util/Linter.hs (59 lines of code) (raw):
-- Copyright (c) Facebook, Inc. and its affiliates.
module Util.Linter
( LintError(..)
, Severity(..)
, LintFormat(..)
, parseLintFormat
, reportLintErrors
) where
import Data.Aeson (ToJSON(..))
import qualified Data.Aeson as Aeson
import qualified Data.ByteString.Lazy.Char8 as ByteString
import Data.Text (Text)
import qualified Data.Text as Text
import qualified Data.Text.IO as Text
import GHC.Generics
import Options.Applicative
import TextShow
import Util.Text
-- Lint Error Types ------------------------------------------------------------
data LintError = LintError
{ source_file :: FilePath
, source_line :: Int
, source_col :: Int
, severity :: Severity
, message :: Text
} deriving (Show, Eq, Generic)
instance ToJSON LintError
data Severity = Warning | Error
deriving (Show, Eq, Generic)
instance ToJSON Severity where
toJSON Warning = Aeson.String "warning"
toJSON Error = Aeson.String "error"
-- Options and Parsers ---------------------------------------------------------
data LintFormat
= HumanReadable
| JsonDump
parseLintFormat :: Parser LintFormat
parseLintFormat = flag HumanReadable JsonDump $ mconcat
[ long "json"
, help "Dump output as JSON"
]
-- Outputs ---------------------------------------------------------------------
reportLintErrors :: LintFormat -> [LintError] -> IO ()
reportLintErrors HumanReadable errors
| null errors = Text.putStrLn "No errors."
| otherwise = mapM_ (Text.putStrLn . renderLintError) errors
reportLintErrors JsonDump errors = ByteString.putStrLn $ Aeson.encode errors
renderLintError :: LintError -> Text
renderLintError LintError{..} = Text.unlines $
colorize
(Text.pack source_file <> ":" <> showt source_line <> ":" <>
showt source_col <> ": " <> Text.pack (show severity)) :
wrapText 2 80 message
where
colorize x = case severity of
-- Warning -> Yellow
Warning -> "\ESC[33m" <> x <> "\ESC[0m"
-- Error -> Red
Error -> "\ESC[31m" <> x <> "\ESC[0m"