{-# LANGUAGE DataKinds #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}

-- | Internal module to define 'Rule', so that its constructor can be visible
-- to other implementation code.
module Test.HMock.Internal.Rule where

import Data.Kind (Constraint, Type)
import GHC.TypeLits (Symbol)
import {-# SOURCE #-} Test.HMock.Internal.State (MockT)
import Test.HMock.Mockable (MockableBase (..))

-- | A way to match an entire action, using conditions that might depend on the
-- relationship between arguments.
data WholeMethodMatcher cls name m r where
  JustMatcher :: Matcher cls name m r -> WholeMethodMatcher cls name m r
  SuchThat ::
    Matcher cls name m r ->
    (Action cls name m r -> Bool) ->
    WholeMethodMatcher cls name m r

-- | Displays a WholeMethodMatcher.  The predicate isn't showable, but we can at
-- least indicate whether there is one present.
showWholeMatcher ::
  MockableBase cls =>
  Maybe (Action cls name m a) ->
  WholeMethodMatcher cls name m b ->
  String
showWholeMatcher :: Maybe (Action cls name m a)
-> WholeMethodMatcher cls name m b -> String
showWholeMatcher Maybe (Action cls name m a)
a (JustMatcher Matcher cls name m b
m) = Maybe (Action cls name m a) -> Matcher cls name m b -> String
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) a b.
MockableBase cls =>
Maybe (Action cls name m a) -> Matcher cls name m b -> String
showMatcher Maybe (Action cls name m a)
a Matcher cls name m b
m
showWholeMatcher Maybe (Action cls name m a)
a (Matcher cls name m b
m `SuchThat` Action cls name m b -> Bool
_) =
  Maybe (Action cls name m a) -> Matcher cls name m b -> String
forall (cls :: (* -> *) -> Constraint) (name :: Symbol)
       (m :: * -> *) a b.
MockableBase cls =>
Maybe (Action cls name m a) -> Matcher cls name m b -> String
showMatcher Maybe (Action cls name m a)
a Matcher cls name m b
m String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
" (with whole method matcher)"

-- | A rule for matching a method and responding to it when it matches.
--
-- The method may be matched by providing either an 'Action' to match exactly,
-- or a 'Matcher'.  Exact matching is only available when all method arguments
--
-- A 'Rule' may have zero or more responses, which are attached using
-- 'Test.HMock.Rule.|->' and 'Test.HMock.Rule.|=>'.  If there are no responses
-- for a 'Rule', then there must be a default response for that action, and it
-- is used.  If more than one response is added, the rule will perform the
-- responses in order, repeating the last response if there are additional
-- matches.
--
-- Example:
--
-- @
-- 'Test.HMock.ExpectContext.expect' $
--   GetLine_ 'Test.HMock.anything'
--     'Test.HMock.Rule.|->' "hello"
--     'Test.HMock.Rule.|=>' \(GetLine prompt) -> "The prompt was " ++ prompt
--     'Test.HMock.Rule.|->' "quit"
-- @
data
  Rule
    (cls :: (Type -> Type) -> Constraint)
    (name :: Symbol)
    (m :: Type -> Type)
    (r :: Type)
  where
  (:=>) ::
    WholeMethodMatcher cls name m r ->
    [Action cls name m r -> MockT m r] ->
    Rule cls name m r