packages feed

generic-case-0.1.0.0: test/Generics/Case/Custom/OneParamTypeSpec.hs

{-# LANGUAGE DeriveGeneric #-}

module Generics.Case.Custom.OneParamTypeSpec (spec) where

import qualified GHC.Generics as G
import Generics.Case
import Generics.Chain
import qualified Generics.SOP as SOP
import qualified Test.Hspec as H
import qualified Test.QuickCheck as Q
import Test.QuickCheck.Function
import Util

data OneParamType a
  = OPT1 a
  | OPT2 (Maybe a)
  | OPT3 a a
  deriving (Show, Eq, G.Generic)

instance SOP.Generic (OneParamType a)

instance (Q.Arbitrary a) => Q.Arbitrary (OneParamType a) where
  arbitrary =
    Q.oneof
      [ OPT1 <$> Q.arbitrary
      , OPT2 <$> Q.arbitrary
      , OPT3 <$> Q.arbitrary <*> Q.arbitrary
      ]
  shrink = Q.genericShrink

type OPTFn a r = OneParamType a -> (a -> r) -> (Maybe a -> r) -> (a -> a -> r) -> r

type FunArgs a r = '[OneParamType a, Fun a r, Fun (Maybe a) r, Fun a (Fun a r)]

type OPTFun a r = Chain (FunArgs a r) r

manual :: OPTFn a r
manual opt fromA fromM fromAs = case opt of
  OPT1 a -> fromA a
  OPT2 m -> fromM m
  OPT3 a1 a2 -> fromAs a1 a2

gopt :: forall a r. OPTFn a r
gopt = gcase @(OneParamType a)

specOPT ::
  forall a r.
  ( Show a
  , Function a
  , Q.CoArbitrary a
  , Q.Arbitrary a
  , Q.Arbitrary r
  , Show r
  , Eq r
  ) =>
  String ->
  OPTFn a r ->
  H.Spec
specOPT name f =
  specG @(FunArgs a r)
    ("manual", mkFn manual)
    (name, mkFn f)

mkFn ::
  forall a r.
  OPTFn a r ->
  OPTFun a r
mkFn f m f1 f2 f3 = f m (applyFun f1) (applyFun f2) (applyFun <$> applyFun f3)

spec :: H.Spec
spec = do
  H.describe "OneParamType () -> Char" $ do
    specOPT @() @Char "gopt" gopt
  H.describe "OneParamType Char -> Either String ()" $ do
    specOPT @Char @(Either String ()) "gopt" gopt
  H.describe "OneParamType String -> (Int, Either Integer Int)" $ do
    specOPT @String @(Int, Either Integer Int) "gopt" gopt
  H.describe "OneParamType [Maybe (Int, String)] -> (Int, [Either (Maybe ()) String])" $ do
    specOPT @[Maybe (Int, String)] @(Int, [Either (Maybe ()) String]) "gopt" gopt