{-# LANGUAGE CPP #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE TemplateHaskell #-}

module Clash.Tests.MaybeX where

import Clash.XException (errorX)
import Clash.XException.MaybeX (MaybeX(IsX, IsDefined), toMaybeX)

#if !MIN_VERSION_base(4,18,0)
import Control.Applicative (liftA2)
#endif
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.TH

data ABC = A | B Int deriving Show

x :: MaybeX a
x = errorX "X"

isX :: MaybeX a -> Bool
isX = \case
  IsDefined {} -> False
  IsX {} -> True

isDefined :: MaybeX a -> Bool
isDefined = not . isX

case_showNoParens :: Assertion
case_showNoParens = show (toMaybeX A) @?= "IsDefined A"

case_showParens :: Assertion
case_showParens = show (toMaybeX (B 0)) @?= "IsDefined (B 0)"

case_pureDefined :: Assertion
case_pureDefined = assertBool "defined value resolves to IsDefined" (isDefined (pure 'a'))

case_pureX :: Assertion
case_pureX = assertBool "pure catches X" (isX (pure x))

case_Fmap :: Assertion
case_Fmap = assertBool "fmap" (isDefined (const () <$> pure 'a'))

case_strictFmap :: Assertion
case_strictFmap = assertBool "fmap is strict in X" (isX (const () <$> pure x))

case_liftA2 :: Assertion
case_liftA2 = assertBool "liftA2"  (isDefined (liftA2 (\_ _ -> ()) (pure 'a') (pure 'b')))

case_strictLiftA2 :: Assertion
case_strictLiftA2 = do
  assertBool "liftA2 is strict in X (left)"  (isX (liftA2 (\_ _ -> ()) (pure x) (pure 'b')))
  assertBool "liftA2 is strict in X (right)" (isX (liftA2 (\_ _ -> ()) (pure 'a') (pure x)))
  assertBool "liftA2 is strict in X (both)"  (isX (liftA2 (\_ _ -> ()) (pure x) (pure x)))

tests :: TestTree
tests = $(testGroupGenerator)
