ema-0.8.0.0: src/Ema/Route/Generic/Verification.hs
{-# LANGUAGE UndecidableInstances #-}
module Ema.Route.Generic.Verification (
type VerifyModels,
type VerifyRoutes,
) where
import Data.Generics.Product.Any (HasAny)
import GHC.TypeLits
{- | @VerifyModels model routeModels lookups@ verifies the given @model@ to ensure that there
exists a valid @HasSubModels@ instance for the given combination of (model, routeModels, lookups).
-}
type family VerifyModels model (subModels :: [Type]) (lookups :: [Type]) :: Constraint where
VerifyModels m '[] '[] = ()
VerifyModels m '[] t =
TypeError
( 'Text "'WithSubModels' has extra unnecessary types: " ':$$: 'Text "" ':$$: 'Text "\t" ':<>: 'ShowType t)
VerifyModels m f '[] =
TypeError
( 'Text "'WithSubModels' is missing submodel types: " ':$$: 'Text "" ':$$: 'Text "\t" ':<>: 'ShowType f)
VerifyModels model (model ': fs) (model ': ss) =
-- This checks the simple case that (the model ~ the submodel),
-- because it doesn't necessarily have to have a generic instance in this case.
-- After that, we can /assume/ the model has a generic instance to allow us to inspect its
-- structure statically to verify that the correct submodel exists.
VerifyModels model fs ss
VerifyModels model (subModel ': subModels) (sel ': sels) =
( HasAny sel model model subModel subModel
, VerifyModels model subModels sels
)
{- | @VerifyRoutes route rep subroutes@ verifies the given @route@ to ensure that there
exists a valid @HasSubRoutes@ instance for @route@ given its @rep@ and the @subroutes@ it is generic-deriving from.
Invariant: code ~ Code route
-}
type family VerifyRoutes (rcode :: [Type]) (subRoutes :: [Type]) :: Constraint where
VerifyRoutes '[] '[] = ()
-- Inconsistent lengths
VerifyRoutes '[] t =
TypeError
( 'Text "'WithSubRoutes' has extra unnecessary types: " ':$$: 'Text "" ':$$: 'Text "\t" ':<>: 'ShowType t)
VerifyRoutes t '[] =
TypeError
( 'Text "'WithSubRoutes' is missing subroutes for:"
':$$: 'Text ""
':$$: ( 'Text "\t" ':<>: 'ShowType t)
)
-- Subroute rep is unit (REVIEW: this case not strictly necessary anymore; should it be removed?)
VerifyRoutes (() ': rs) (() : rs') = VerifyRoutes rs rs'
VerifyRoutes (r' ': rs) (() : rs') =
TypeError
( 'Text "A 'WithSubRoutes' entry is '()' instead of the expected: "
':$$: 'ShowType r'
)
VerifyRoutes (r1 ': rs) (r2 ': rs') = (Coercible r1 r2, VerifyRoutes rs rs')