{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Service.HtmlChecker.Response where
import Control.Applicative ((<|>))
import Data.Aeson (decode, encode)
import Data.Aeson.TH (defaultOptions, deriveJSON,
fieldLabelModifier)
import qualified Data.ByteString.Lazy.Char8 as L8
import Data.Char (toUpper)
import Data.List (intercalate)
import Data.Maybe (catMaybes)
import qualified Data.Text as T
data Message = Message
{ type_ :: Maybe String
, subType :: Maybe String
, message :: Maybe String
, extract :: Maybe String
, firstLine :: Maybe Int
, firstColumn :: Maybe Int
, lastLine :: Maybe Int
, lastColumn :: Maybe Int
, hiliteStart :: Maybe Int
, hiliteLength :: Maybe Int
} deriving (Eq, Read, Show)
deriveJSON (defaultOptions
{ fieldLabelModifier = \s -> case s of
"type_" -> "type"
t -> t
} ) ''Message
data ValidationResult = ValidationResult
{ messages :: [Message]
} deriving (Eq, Read, Show)
deriveJSON defaultOptions ''ValidationResult
decodeResult :: L8.ByteString -> Maybe ValidationResult
decodeResult resultJson = decode resultJson
getMessages :: ValidationResult -> [Message]
getMessages = messages
countErrors :: Maybe ValidationResult -> Int
countErrors (Just result) = length $ filter isError $ getMessages result
countErrors Nothing = 1
isError :: Message -> Bool
isError msg = case (type_ msg) of
Just t | (map toUpper t) == "ERROR" -> True
| otherwise -> False
Nothing -> True
showMessage :: Bool -> Bool -> Message -> String
showMessage isOneLine isColored = case (isOneLine, isColored) of
(True, True) -> showMessageInOneLineWithColor
(True, _ ) -> showMessageInOneLine
(_ , True) -> showMessageInClassicWithColor
_ -> showMessageInClassic
showMessageInClassic :: Message -> String
showMessageInClassic = showMessageInClassic' getType getExtract getHilite
showMessageInClassicWithColor :: Message -> String
showMessageInClassicWithColor = showMessageInClassic' getTypeWithColor
getExtractWithColor
getHiliteWithColor
showMessageInClassic'
:: (Message -> Maybe String)
-> (Message -> Maybe String)
-> (Message -> Maybe String)
-> Message
-> String
showMessageInClassic' typeGetter extractGetter hiliteGetter msg =
unlines $ catMaybes $ map ($ msg) [line1, line2, line3, line4]
where
line1 msg = (\t m -> t ++ " " ++ m) <$> typeGetter msg <*> getMessage msg
line2 = getPoint
line3 = extractGetter
line4 = hiliteGetter
showMessageInOneLine :: Message -> String
showMessageInOneLine = showMessageInOneLine' getType
showMessageInOneLineWithColor :: Message -> String
showMessageInOneLineWithColor = showMessageInOneLine' getTypeWithColor
showMessageInOneLine' :: (Message -> Maybe String) -> Message -> String
showMessageInOneLine' typeGetter msg =
unwords $ catMaybes $ map ($ msg) [typeGetter, getMessage, getPoint]
getType :: Message -> Maybe String
getType msg =
(\t -> "[" ++ map toUpper t ++ "]") <$> (subType msg <|> type_ msg)
getTypeWithColor :: Message -> Maybe String
getTypeWithColor msg = paint <$> getType msg
where
paint s | s `elem` ["[ERROR]", "[FATAL]"] = (goBold . goRed) s
| s `elem` ["[INFO]", "[WARNING]"] = (goBold . goYellow) s
| otherwise = id s
getMessage :: Message -> Maybe String
getMessage msg = (T.unpack . T.strip . T.pack) <$> message msg
getPoint :: Message -> Maybe String
getPoint msg =
case (firstLine msg, firstColumn msg, lastLine msg, lastColumn msg) of
(Just fl, Just fc, Just ll, Just lc) -> Just (showFromTo fl fc ll lc)
(_, Just fc, Just ll, Just lc) -> Just (showFromTo ll fc ll lc)
(_, _, Just ll, Just lc) -> Just (showAt ll lc)
_ -> Nothing
where
showFromTo fl fc ll lc =
"From " ++ showPoint fl fc ++ "; to " ++ showPoint ll lc
showAt l c = "At " ++ showPoint l c
showPoint l c = "line " ++ show l ++ ", column " ++ show c
getExtract :: Message -> Maybe String
getExtract msg = unescapeSomeSpecialChar <$> extract msg
getExtractWithColor :: Message -> Maybe String
getExtractWithColor msg = do
extract' <- getExtract msg
prefixLen <- hiliteStart msg
hiliteLen <- hiliteLength msg
let prefix = take prefixLen extract'
let hilite = goCyan $ take hiliteLen (drop prefixLen extract')
let suffix = drop (prefixLen + hiliteLen) extract'
return (prefix ++ hilite ++ suffix)
getHilite :: Message -> Maybe String
getHilite msg = (++) <$> padding <*> indicator
where
padding = (\n -> replicate n ' ') <$> hiliteStart msg
indicator = (\n -> replicate n '^') <$> hiliteLength msg
getHiliteWithColor :: Message -> Maybe String
getHiliteWithColor msg = goCyan <$> getHilite msg
unescapeSomeSpecialChar :: String -> String
unescapeSomeSpecialChar (x : xs)
| x `elem` ['\r', '\n', '\t'] = ' ' : unescapeSomeSpecialChar xs
| otherwise = x : unescapeSomeSpecialChar xs
unescapeSomeSpecialChar "" = ""
goBold :: String -> String
goBold s = "\x1b[1m" ++ s ++ "\x1b[0m"
goRed :: String -> String
goRed s = "\x1b[31m" ++ s ++ "\x1b[0m"
goYellow :: String -> String
goYellow s = "\x1b[33m" ++ s ++ "\x1b[0m"
goBlue :: String -> String
goBlue s = "\x1b[34m" ++ s ++ "\x1b[0m"
goCyan :: String -> String
goCyan s = "\x1b[36m" ++ s ++ "\x1b[0m"