packages feed

mockcat-0.3.0.0: src/Test/MockCat/Param.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilyDependencies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- | This module is a parameter of the mock function.
--
-- This parameter can be used when creating and verifying the mock.
module Test.MockCat.Param
  ( Param(..),
    value,
    param,
    (|>),
    expect,
    expect_,
    any
  )
where

import Test.MockCat.Cons ((:>) (..))
import Unsafe.Coerce (unsafeCoerce)
import Prelude hiding (any)

data Param v
  = ExpectValue v
  | ExpectCondition (v -> Bool) String

instance (Eq a) => Eq (Param a) where
  (ExpectValue a) == (ExpectValue b) = a == b
  (ExpectValue a) == (ExpectCondition m2 _) = m2 a
  (ExpectCondition m1 _) == (ExpectValue b) = m1 b
  (ExpectCondition _ l1) == (ExpectCondition _ l2) = l1 == l2

type family ShowResult a where
  ShowResult String = String
  ShowResult a = String

class ShowParam a where
  showParam :: a -> ShowResult a

instance {-# OVERLAPPING #-} ShowParam (Param String) where
  showParam (ExpectCondition _ l) = l
  showParam (ExpectValue a) = a

instance {-# INCOHERENT #-} (Show a) => ShowParam (Param a) where
  showParam (ExpectCondition _ l) = l
  showParam (ExpectValue a) = show a

instance (ShowParam (Param a)) => Show (Param a) where
  show = showParam

value :: Param v -> v
value (ExpectValue a) = a
value _ = error "not implement"

param :: v -> Param v
param = ExpectValue

class ConsGen a b r | a b -> r where
  (|>) :: a -> b -> r

instance {-# OVERLAPPING #-} (Param a ~ a', (Param b :> c) ~ bc) => ConsGen a (Param b :> c) (a' :> bc) where
  (|>) a = (:>) (param a)
instance {-# OVERLAPPING #-} ((Param b :> c) ~ bc) => ConsGen (Param a) (Param b :> c) (Param a :> bc) where
  (|>) = (:>)
instance ConsGen (Param a) (Param b) (Param a :> Param b) where
  (|>) = (:>)
instance {-# OVERLAPPABLE #-} ((Param b) ~ b') => ConsGen (Param a) b (Param a :> b') where
  (|>) a b = (:>) a (param b)
instance {-# OVERLAPPABLE #-} ((Param a) ~ a') => ConsGen a (Param b) (a' :> Param b) where
  (|>) a = (:>) (param a)
instance {-# OVERLAPPABLE #-} (Param a ~ a', Param b ~ b') => ConsGen a b (a' :> b') where
  (|>) a b = (:>) (param a) (param b)

infixr 8 |>

-- | Make a parameter to which any value is expected to apply.
any :: Param a
any = unsafeCoerce (ExpectCondition (const True) "any")

{- | Create a conditional parameter.

   When applying a mock function, if the argument does not satisfy this condition, an error occurs.

   In this case, the specified label is included in the error message.
-}
expect :: (a -> Bool) -> String -> Param a
expect = ExpectCondition

{- | Create a conditional parameter.

  In applied a mock function, if the argument does not satisfy this condition, an error occurs.

  Unlike @'expect'@, it does not require a label, but the error message is displayed as [some condition].
-}
expect_ :: (a -> Bool) -> Param a
expect_ f = ExpectCondition f "[some condition]"