module Hadolint.Formatter.Format
  ( severityText,
    stripNewlines,
    errorMessageLine,
    errorPosition,
    errorPositionPretty,
    Text.Megaparsec.Error.errorBundlePretty,
    Result (..),
    isEmpty,
    toResult,
  )
where

import Data.List (sort)
import qualified Data.List.NonEmpty as NE
import Data.Monoid (Monoid)
import Data.Semigroup
import Data.Sequence (Seq)
import qualified Data.Sequence as Seq
import Hadolint.Rules
import ShellCheck.Interface
import Text.Megaparsec (Stream (..), pstateSourcePos)
import Text.Megaparsec.Error
import Text.Megaparsec.Pos (SourcePos, sourcePosPretty)

data Result s e = Result
  { Result s e -> Seq (ParseErrorBundle s e)
errors :: !(Seq (ParseErrorBundle s e)),
    Result s e -> Seq RuleCheck
checks :: !(Seq RuleCheck)
  }

instance Semigroup (Result s e) where
  (Result e1 :: Seq (ParseErrorBundle s e)
e1 c1 :: Seq RuleCheck
c1) <> :: Result s e -> Result s e -> Result s e
<> (Result e2 :: Seq (ParseErrorBundle s e)
e2 c2 :: Seq RuleCheck
c2) = Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
forall s e.
Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
Result (Seq (ParseErrorBundle s e)
e1 Seq (ParseErrorBundle s e)
-> Seq (ParseErrorBundle s e) -> Seq (ParseErrorBundle s e)
forall a. Semigroup a => a -> a -> a
<> Seq (ParseErrorBundle s e)
e2) (Seq RuleCheck
c1 Seq RuleCheck -> Seq RuleCheck -> Seq RuleCheck
forall a. Semigroup a => a -> a -> a
<> Seq RuleCheck
c2)

instance Monoid (Result s e) where
  mappend :: Result s e -> Result s e -> Result s e
mappend = Result s e -> Result s e -> Result s e
forall a. Semigroup a => a -> a -> a
(<>)
  mempty :: Result s e
mempty = Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
forall s e.
Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
Result Seq (ParseErrorBundle s e)
forall a. Monoid a => a
mempty Seq RuleCheck
forall a. Monoid a => a
mempty

isEmpty :: Result s e -> Bool
isEmpty :: Result s e -> Bool
isEmpty (Result Seq.Empty Seq.Empty) = Bool
True
isEmpty _ = Bool
False

toResult :: Either (ParseErrorBundle s e) [RuleCheck] -> Result s e
toResult :: Either (ParseErrorBundle s e) [RuleCheck] -> Result s e
toResult res :: Either (ParseErrorBundle s e) [RuleCheck]
res =
  case Either (ParseErrorBundle s e) [RuleCheck]
res of
    Left err :: ParseErrorBundle s e
err -> Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
forall s e.
Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
Result (ParseErrorBundle s e -> Seq (ParseErrorBundle s e)
forall a. a -> Seq a
Seq.singleton ParseErrorBundle s e
err) Seq RuleCheck
forall a. Monoid a => a
mempty
    Right c :: [RuleCheck]
c -> Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
forall s e.
Seq (ParseErrorBundle s e) -> Seq RuleCheck -> Result s e
Result Seq (ParseErrorBundle s e)
forall a. Monoid a => a
mempty ([RuleCheck] -> Seq RuleCheck
forall a. [a] -> Seq a
Seq.fromList ([RuleCheck] -> [RuleCheck]
forall a. Ord a => [a] -> [a]
sort [RuleCheck]
c))

severityText :: Severity -> String
severityText :: Severity -> String
severityText s :: Severity
s =
  case Severity
s of
    ErrorC -> "error"
    WarningC -> "warning"
    InfoC -> "info"
    StyleC -> "style"

stripNewlines :: String -> String
stripNewlines :: String -> String
stripNewlines =
  (Char -> Char) -> String -> String
forall a b. (a -> b) -> [a] -> [b]
map
    ( \c :: Char
c ->
        if Char
c Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== '\n'
          then ' '
          else Char
c
    )

errorMessageLine :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
errorMessageLine :: ParseErrorBundle s e -> String
errorMessageLine err :: ParseErrorBundle s e
err@(ParseErrorBundle e :: NonEmpty (ParseError s e)
e _) =
  ParseErrorBundle s e -> String
forall s e. Stream s => ParseErrorBundle s e -> String
errorPositionPretty ParseErrorBundle s e
err String -> String -> String
forall a. [a] -> [a] -> [a]
++ " " String -> String -> String
forall a. [a] -> [a] -> [a]
++ ParseError s e -> String
forall s e.
(Stream s, ShowErrorComponent e) =>
ParseError s e -> String
parseErrorTextPretty (NonEmpty (ParseError s e) -> ParseError s e
forall a. NonEmpty a -> a
NE.head NonEmpty (ParseError s e)
e)

errorPositionPretty :: Stream s => ParseErrorBundle s e -> String
errorPositionPretty :: ParseErrorBundle s e -> String
errorPositionPretty err :: ParseErrorBundle s e
err = SourcePos -> String
sourcePosPretty (ParseErrorBundle s e -> SourcePos
forall s e. Stream s => ParseErrorBundle s e -> SourcePos
errorPosition ParseErrorBundle s e
err)

errorPosition :: Stream s => ParseErrorBundle s e -> Text.Megaparsec.Pos.SourcePos
errorPosition :: ParseErrorBundle s e -> SourcePos
errorPosition (ParseErrorBundle e :: NonEmpty (ParseError s e)
e s :: PosState s
s) =
  let (_, posState :: PosState s
posState) = Int -> PosState s -> (String, PosState s)
forall s. Stream s => Int -> PosState s -> (String, PosState s)
reachOffset (ParseError s e -> Int
forall s e. ParseError s e -> Int
errorOffset (NonEmpty (ParseError s e) -> ParseError s e
forall a. NonEmpty a -> a
NE.head NonEmpty (ParseError s e)
e)) PosState s
s
   in PosState s -> SourcePos
forall s. PosState s -> SourcePos
pstateSourcePos PosState s
posState