{-|
  This module provides a Template Haskell function for automatically generating
  reified typeclass dictionaries for use with "Control.Monad.TestFixture".
  These generated dictionaries can be used with functions like
  'Control.Monad.TestFixture.unTestFixture' and
  'Control.Monad.TestFixture.logTestFixture' to quickly implement monadic
  typeclasses in a way that can be used to “stub out” functionality in unit
  tests.

  The 'mkFixture' function is a Template Haskell code generation tool, which
  generates three things:

    1. A record type that represents a reified typeclass dictionary (or set of
       typeclass dictionaries). The record contains fields that correspond to
       the methods of the provided typeclasses, with ordinary method names
       prefixed with a @_@ character and infix method names prefixed with a @~@
       character.

    2. A 'Default' instance for the generated record type, which automatically
       fills all fields with stub implementations that will throw using
       'unimplemented'.

    3. Typeclass implementations for all of the provided typeclasses using
       'TestFixture' and the generated record type that defer to the
       implementations provided through the reified dictionary.

  In practice, this is used for generate “fixture” types that are used within
  tests. For example, consider some typeclasses that encode side-effectful
  monadic operations:

  > class Monad m => DB m where
  >   fetchRecord :: DBRecord a => Id a -> m (Either DBError a)
  >   insertRecord :: DBRecord a => a -> m (Either DBError (Id a))
  >
  > class Monad m => HTTP m where
  >   sendRequest :: HTTPRequest -> m (Either HTTPError HTTPResponse)

  The typeclasses may have relatively straightforward instances for 'IO'.
  However, one of the main values of them is that alternative instances may be
  provided in unit tests, which is what 'TestFixture' provides. Therefore,
  one might use 'mkFixture' to create some utilities for stubbing these
  typeclasses out:

  > mkFixture "Fixture" [ts| DB, HTTP |]

  This generates code much like the following:

  > data Fixture m =
  >   { _fetchRecord :: DBRecord a => Id a -> m (Either DBError a)
  >   , _insertRecord :: DBRecord a => a -> m (Either DBError (Id a))
  >   , _sendRequest :: HTTPRequest -> m (Either HTTPError HTTPResponse)
  >   }
  >
  > instance Default (Fixture m) where
  >   def = Fixture
  >     { _fetchRecord = unimplemented "_fetchRecord"
  >     , _insertRecord = unimplemented "_insertRecord"
  >     , _sendRequest = unimplemented "_sendRequest"
  >     }
  >
  > type FixturePure = Fixture (TestFixture Fixture () ())
  > type FixtureLog log = Fixture (TestFixture Fixture log ())
  > type FixtureState state = Fixture (TestFixture Fixture () state)
  > type FixtureLogState log state = Fixture (TestFixture Fixture log state)
  >
  > type FixturePureT m = Fixture (TestFixture Fixture () () m)
  > type FixtureLogT log m = Fixture (TestFixture Fixture log () m)
  > type FixtureStateT state m = Fixture (TestFixture Fixture () state m)
  > type FixtureLogStateT log state m = Fixture (TestFixtureT Fixture log state m)
  >
  > instance Monad m => DB (TestFixtureT Fixture w s m) where
  >   fetchRecord r = do
  >     fn <- asks _fetchRecord
  >     fn r
  >   insertRecord r = do
  >     fn <- asks _insertRecord
  >     fn r
  >
  > instance Monad m => HTTP (TestFixtureT Fixture w s m) where
  >   sendRequest r = do
  >     fn <- asks _sendRequest
  >     fn r

  This type can then be used in tandem with "Control.Monad.TestFixture" to
  create stubbed typeclass instances and run computations using them.
-}
module Control.Monad.TestFixture.TH
  ( mkFixture
  , def
  , ts
  ) where

import Control.Monad.TestFixture.TH.Internal (mkFixture)
import Control.Monad.TestFixture.TH.Internal.TypesQuasi (ts)
import Data.Default.Class (def)