packages feed

ema-0.8.0.0: test/type-errors/Deriving.hs

{-# LANGUAGE CPP #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

module Deriving where

import Data.Proxy (Proxy)
import Data.Text (Text)
import Ema.Route.Class
import Ema.Route.Generic
import Ema.Route.Generic.TH
import GHC.Generics qualified as GHC
import Generics.SOP qualified as SOP
import Text.RawString.QQ (r)

#define ENABLE_SPEC

----------------------------------------
-- Subroute verification
----------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data BadRoute = BR_1 Int String | BR_2 String
data M = M { m1 :: (), m2 :: () } deriving stock GHC.Generic

deriveGeneric ''BadRoute
-- Subroutes should not have constructors with multiple fields
-- Expect: MultiRoute: too many arguments
deriveIsRoute ''BadRoute [t|
    '[ WithModel M
     ]
  |]
#endif
#define ENABLE_SPEC

-----------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R_1 () | R_2 ()
deriveGeneric ''R
-- WithSubRoutes' list should not be shorter than number of route constructors
-- Expect:
{-
'WithSubRoutes' is missing subroutes for:

  '[()]
-}
deriveIsRoute ''R [t|
  '[ WithSubRoutes '[ () ] ]
  |]
#endif
#define ENABLE_SPEC

-----------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R_1 () | R_2 ()
deriveGeneric ''R
-- WithSubRoutes' list should not be longer than number of route constructors
-- Expect:
{-
'WithSubRoutes' has extra unnecessary types:

  '[Int]
-}
deriveIsRoute ''R [t|
  '[ WithSubRoutes '[ (), (), Int ] ]
  |]
#endif
#define ENABLE_SPEC

------------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R_1 Int | R_2
deriveGeneric ''R
-- constructors should either be empty or contain () when 'WithSubRoutes' specifies ()
-- Expect:
{-
A 'WithSubRoutes' entry is '()' instead of the expected:
Int
-}
deriveIsRoute ''R [t|
  '[ WithSubRoutes '[ (), () ] ]
  |]
#endif
#define ENABLE_SPEC

-------------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R_1 | R_2 
deriveGeneric ''R
-- subroute types that are nonisomorphic to what is specified in 'WithSubRoutes' should be illegal
-- Expect:
{-
Couldn't match representation of type ‘()’ with that of ‘Bool’
  arising from a use of ‘routePrism’
-}
deriveIsRoute ''R [t|
  ' [ WithSubRoutes '[ (), Bool] ]
  |]
#endif
#define ENABLE_SPEC

-------------------------------------------

-- subroute types that are the same as what is specified in 'WithSubRoutes' should typecheck"
data RSubIso = RSubIso_1 | RSubIso_2
deriveGeneric ''RSubIso
deriveIsRoute
  ''RSubIso
  [t|
    '[WithSubRoutes '[(), ()]]
    |]

-------------------------------------------

-- FIXME: This test is broken.
#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
routeSpec "subroute types that are an unwrapped representation of what is specified in 'WithSubRoutes' should typecheck | ( empty constructor <-> () ) special case"
  (niceRoute ''() ''())
  [t|
    '[ WithModel (NiceNamedM () ())
     , WithSubRoutes '[ (), PlainR_NiceNamedM ]
     ]
  |]
  [r|
  |]
#endif
#define ENABLE_SPEC

-------------------------------------------

-- Subroute types that are an unwrapped representation of what is specified in
-- 'WithSubRoutes' should typecheck | (wrapper deriving /newtype/ GHC.Generic)
-- case"
data RSubNewtype = RSubNewtype_1 | RSubNewtype_2
deriveGeneric ''RSubNewtype
newtype WrapNewtype a = WrapNewtype a
  deriving newtype (GHC.Generic, IsRoute)
deriveIsRoute
  ''RSubNewtype
  [t|
    '[ WithSubRoutes '[(), WrapNewtype ()]
     ]
    |]

-------------------------------------------
-- Submodel verification
-------------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R1 | R2 
deriveGeneric ''R 
data M = M { m1 :: (), m2 :: () } deriving stock GHC.Generic
-- Submodel selectors should not be less than number of subroutes
-- Expect:
{-
'WithSubModels' is missing submodel types:
  '[()]
-}
deriveIsRoute ''R 
  [t|
    '[ WithModel M
     , WithSubModels '[ Proxy "m1" ]
     ]
  |]
#endif
#define ENABLE_SPEC

-------------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R1 | R2 
deriveGeneric ''R 
data M = M { m1 :: (), m2 :: () }
  deriving stock GHC.Generic
-- submodel selectors should not outnumber number of subroutes
-- Expect:
{-
'WithSubModels' has extra unnecessary types:

  '[Proxy "m2"]
-}
deriveIsRoute ''R
  [t|
    '[ WithModel M
     , WithSubModels '[ Proxy "m1", Proxy "m2", Proxy "m2" ]
     ]
  |]
#endif
#define ENABLE_SPEC

-------------------------------------------
data RSubMSelf = RSubMSelf1 | RSubMSelf2 RSubMSelf
  deriving stock (GHC.Generic)
deriveGeneric ''RSubMSelf
data MSelf = MSelf {mself1 :: (), mself2 :: ()}
  deriving stock (GHC.Generic)

-- submodel type selectors be able to reference the model itself if they are of the same type
deriveIsRoute
  ''RSubMSelf
  [t|
    '[ WithModel MSelf
     , WithSubModels '[Proxy "mself1", MSelf]
     ]
    |]

-------------------------------------------

#undef ENABLE_SPEC
#ifdef ENABLE_SPEC
data R = R1 | R2
deriveGeneric ''R
data M = M { m1 :: (), m2 :: () }
  deriving stock GHC.Generic
-- submodel type selectors must, at a minimum, refer to a model field of a matching type
-- Expect:
{-
Couldn't match type ‘Bool’ with ‘()’
-}
deriveIsRoute ''R
  [t|
    '[ WithModel M
     , WithSubModels '[ Proxy "m1", Bool ]
     ]
  |]
#endif
#define ENABLE_SPEC

-----------------------------------------