hspec-core-2.9.3: src/Test/Hspec/Core/Formatters/Pretty.hs
{-# LANGUAGE CPP #-}
{-# LANGUAGE OverloadedStrings #-}
module Test.Hspec.Core.Formatters.Pretty (
pretty2
#ifdef TEST
, pretty
, recoverString
#endif
) where
import Prelude ()
import Test.Hspec.Core.Compat hiding (shows, intercalate)
import Control.Arrow
import Data.Char
import Data.String
import Data.List (intersperse)
import qualified Text.Show as Show
import Test.Hspec.Core.Formatters.Pretty.Unicode
import Test.Hspec.Core.Formatters.Pretty.Parser
pretty2 :: Bool -> String -> String -> (String, String)
pretty2 unicode expected actual = case (recoverString unicode expected, recoverString unicode actual) of
(Just expected_, Just actual_) -> (expected_, actual_)
_ -> case (pretty unicode expected, pretty unicode actual) of
(Just expected_, Just actual_) -> (expected_, actual_)
#if __GLASGOW_HASKELL__ >= 802
_ -> (expected, actual)
#else
_ -> (rec expected, rec actual)
where
rec = if unicode then urecover else id
urecover :: String -> String
urecover xs = maybe xs ushow $ readMaybe xs
#endif
recoverString :: Bool -> String -> Maybe String
recoverString unicode input = case readMaybe input of
Just r | shouldParseBack r -> Just r
_ -> Nothing
where
shouldParseBack = (&&) <$> all isSafe <*> isMultiLine
isMultiLine = lines >>> length >>> (> 1)
isSafe c = (unicode || isAscii c) && (not $ isControl c) || c == '\n'
pretty :: Bool -> String -> Maybe String
pretty unicode = parseExpression >=> render_
where
render_ :: Expression -> Maybe String
render_ expr = guard (shouldParseBack expr) >> Just (renderExpression unicode expr)
shouldParseBack :: Expression -> Bool
shouldParseBack = go
where
go expr = case expr of
Literal (String _) -> True
Literal _ -> False
Id _ -> False
App (Id _) e -> go e
App _ _ -> False
Parentheses e -> go e
Tuple xs -> any go xs
List xs -> any go xs
Record _ _ -> True
newtype Builder = Builder ShowS
instance Monoid Builder where
mempty = Builder id
#if MIN_VERSION_base(4,11,0)
instance Semigroup Builder where
#endif
Builder xs
#if MIN_VERSION_base(4,11,0)
<>
#else
`mappend`
#endif
Builder ys = Builder (xs . ys)
runBuilder :: Builder -> String
runBuilder (Builder xs) = xs ""
intercalate :: Builder -> [Builder] -> Builder
intercalate x xs = mconcat $ intersperse x xs
shows :: Show a => a -> Builder
shows = Builder . Show.shows
instance IsString Builder where
fromString = Builder . showString
renderExpression :: Bool -> Expression -> String
renderExpression unicode = runBuilder . render
where
renderLiteral lit = case lit of
Char c -> shows c
String str -> if unicode then Builder $ ushows str else shows str
Integer n -> shows n
Rational n -> shows n
render :: Expression -> Builder
render expr = case expr of
Literal lit -> renderLiteral lit
Id name -> fromString name
App a b -> render a <> " " <> render b
Parentheses e@Record{} -> render e
Parentheses e -> "(" <> render e <> ")"
Tuple xs -> "(" <> intercalate ", " (map render xs) <> ")"
List xs -> "[" <> intercalate ", " (map render xs) <> "]"
Record name fields -> fromString name <> " {\n " <> (intercalate ",\n " $ map renderField fields) <> "\n}"
renderField (name, value) = fromString name <> " = " <> render value