{-# LANGUAGE CPP #-}
{-# LANGUAGE ViewPatterns #-}
module Test.Hspec.Core.Formatters.Diff (
  Diff (..)
, diff
#ifdef TEST
, partition
, breakList
#endif
) where

import           Prelude ()
import           Test.Hspec.Core.Compat

import           Data.Char
import           Data.Algorithm.Diff

diff :: String -> String -> [Diff String]
diff :: String -> String -> [Diff String]
diff String
expected String
actual = (Diff [String] -> Diff String) -> [Diff [String]] -> [Diff String]
forall a b. (a -> b) -> [a] -> [b]
map (([String] -> String) -> Diff [String] -> Diff String
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap [String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat) ([Diff [String]] -> [Diff String])
-> [Diff [String]] -> [Diff String]
forall a b. (a -> b) -> a -> b
$ [String] -> [String] -> [Diff [String]]
forall t. Eq t => [t] -> [t] -> [Diff [t]]
getGroupedDiff (String -> [String]
partition String
expected) (String -> [String]
partition String
actual)

partition :: String -> [String]
partition :: String -> [String]
partition = (String -> Bool) -> [String] -> [String]
forall a. (a -> Bool) -> [a] -> [a]
filter (Bool -> Bool
not (Bool -> Bool) -> (String -> Bool) -> String -> Bool
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null) ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [String] -> [String]
mergeBackslashes ([String] -> [String])
-> (String -> [String]) -> String -> [String]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> Bool) -> String -> [String]
forall a. (a -> Bool) -> [a] -> [[a]]
breakList Char -> Bool
isAlphaNum
  where
    mergeBackslashes :: [String] -> [String]
mergeBackslashes [String]
xs = case [String]
xs of
      [Char
'\\'] : (String -> Maybe (String, String)
splitEscape -> Just (String
escape, String
ys)) : [String]
zs -> (String
"\\" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
escape) String -> [String] -> [String]
forall a. a -> [a] -> [a]
: String
ys String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String] -> [String]
mergeBackslashes [String]
zs
      String
z : [String]
zs -> String
z String -> [String] -> [String]
forall a. a -> [a] -> [a]
: [String] -> [String]
mergeBackslashes [String]
zs
      [] -> []

breakList :: (a -> Bool) -> [a] -> [[a]]
breakList :: (a -> Bool) -> [a] -> [[a]]
breakList a -> Bool
_ [] = []
breakList a -> Bool
p [a]
xs = case (a -> Bool) -> [a] -> ([a], [a])
forall a. (a -> Bool) -> [a] -> ([a], [a])
break a -> Bool
p [a]
xs of
  ([a]
y, [a]
ys) -> (a -> [a]) -> [a] -> [[a]]
forall a b. (a -> b) -> [a] -> [b]
map a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return [a]
y [[a]] -> [[a]] -> [[a]]
forall a. [a] -> [a] -> [a]
++ case (a -> Bool) -> [a] -> ([a], [a])
forall a. (a -> Bool) -> [a] -> ([a], [a])
span a -> Bool
p [a]
ys of
    ([a]
z, [a]
zs) -> [a]
z [a] -> [[a]] -> [[a]]
forall (t :: * -> *) a. Foldable t => t a -> [t a] -> [t a]
`cons` (a -> Bool) -> [a] -> [[a]]
forall a. (a -> Bool) -> [a] -> [[a]]
breakList a -> Bool
p [a]
zs
  where
    cons :: t a -> [t a] -> [t a]
cons t a
x
      | t a -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null t a
x = [t a] -> [t a]
forall a. a -> a
id
      | Bool
otherwise = (t a
x t a -> [t a] -> [t a]
forall a. a -> [a] -> [a]
:)

splitEscape :: String -> Maybe (String, String)
splitEscape :: String -> Maybe (String, String)
splitEscape String
xs = String -> Maybe (String, String)
splitNumericEscape String
xs Maybe (String, String)
-> Maybe (String, String) -> Maybe (String, String)
forall (f :: * -> *) a. Alternative f => f a -> f a -> f a
<|> ([Maybe (String, String)] -> Maybe (String, String)
forall (t :: * -> *) (m :: * -> *) a.
(Foldable t, MonadPlus m) =>
t (m a) -> m a
msum ([Maybe (String, String)] -> Maybe (String, String))
-> [Maybe (String, String)] -> Maybe (String, String)
forall a b. (a -> b) -> a -> b
$ (String -> Maybe (String, String))
-> [String] -> [Maybe (String, String)]
forall a b. (a -> b) -> [a] -> [b]
map String -> Maybe (String, String)
split [String]
escapes)
  where
    split :: String -> Maybe (String, String)
    split :: String -> Maybe (String, String)
split String
escape = (,) String
escape (String -> (String, String))
-> Maybe String -> Maybe (String, String)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> String -> String -> Maybe String
forall a. Eq a => [a] -> [a] -> Maybe [a]
stripPrefix String
escape String
xs

splitNumericEscape :: String -> Maybe (String, String)
splitNumericEscape :: String -> Maybe (String, String)
splitNumericEscape String
xs = case (Char -> Bool) -> String -> (String, String)
forall a. (a -> Bool) -> [a] -> ([a], [a])
span Char -> Bool
isDigit String
xs of
  (String
"", String
_) -> Maybe (String, String)
forall a. Maybe a
Nothing
  (String, String)
r -> (String, String) -> Maybe (String, String)
forall a. a -> Maybe a
Just (String, String)
r

escapes :: [String]
escapes :: [String]
escapes = [
    String
"ACK"
  , String
"CAN"
  , String
"DC1"
  , String
"DC2"
  , String
"DC3"
  , String
"DC4"
  , String
"DEL"
  , String
"DLE"
  , String
"ENQ"
  , String
"EOT"
  , String
"ESC"
  , String
"ETB"
  , String
"ETX"
  , String
"NAK"
  , String
"NUL"
  , String
"SOH"
  , String
"STX"
  , String
"SUB"
  , String
"SYN"
  , String
"EM"
  , String
"FS"
  , String
"GS"
  , String
"RS"
  , String
"SI"
  , String
"SO"
  , String
"US"
  , String
"a"
  , String
"b"
  , String
"f"
  , String
"n"
  , String
"r"
  , String
"t"
  , String
"v"
  , String
"&"
  , String
"'"
  , String
"\""
  , String
"\\"
  ]