{-# LANGUAGE DataKinds, KindSignatures, TypeOperators, PolyKinds #-}
{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
{-# LANGUAGE ScopedTypeVariables, AllowAmbiguousTypes, TypeFamilies #-}
{-# LANGUAGE UndecidableInstances, ConstraintKinds, UndecidableSuperClasses #-}
{-# LANGUAGE TypeApplications, CPP #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Database.Selda.FieldSelectors
(FieldType, HasField, IsLabel
) where
import Database.Selda.Generic (Relational)
import Database.Selda.Selectors as S
import Database.Selda.SqlType (SqlType)
import Data.Kind (Constraint)
import GHC.Generics
import GHC.TypeLits
import GHC.OverloadedLabels
type family GetFieldType (f :: * -> *) :: * where
GetFieldType (M1 c i f) = GetFieldType f
GetFieldType (K1 i a) = a
type family GFieldType (a :: * -> *) (b :: *) (name :: Symbol) :: * where
GFieldType (M1 S ('MetaSel ('Just name) su ss ds) f) b name = GetFieldType f
GFieldType (M1 c i a) b name = GFieldType a b name
GFieldType (a :*: b) c name = GFieldType a (GFieldType b c name) name
GFieldType a b name = b
type FieldType name t = GFieldType (Rep t) (NoSuchSelector t name) name
type family NonError (t :: k) :: Constraint where
NonError (NoSuchSelector t s) = TypeError
( 'Text "Row type '" ':<>: 'ShowType t ':<>:
'Text "' has no selector " ':<>: 'ShowType s ':<>: 'Text "."
)
NonError t = ()
data NoSuchSelector (t :: *) (s :: Symbol)
class ( Relational t
, SqlType (FieldType name t)
, GRSel name (Rep t)
, NonError (FieldType name t)) =>
HasField (name :: Symbol) t
instance ( Relational t
, SqlType (FieldType name t)
, GRSel name (Rep t)
, NonError (FieldType name t)) =>
HasField (name :: Symbol) t
instance (Relational t, HasField name t, FieldType name t ~ a) =>
IsLabel name (S.Selector t a) where
#if MIN_VERSION_base(4, 10, 0)
fromLabel = field @name @t
#else
fromLabel _ = field @name @t
#endif
field :: forall name t.
(Relational t, HasField name t)
=> S.Selector t (FieldType name t)
field =
case gSel @name @(Rep t) 0 of
Left n -> unsafeSelector n
_ -> error "unreachable"
class GRSel (s :: Symbol) (f :: * -> *) where
gSel :: Int -> Either Int Int
instance GRSel name (M1 S ('MetaSel ('Just name) su ss ds) f) where
gSel = Left
instance {-# OVERLAPPABLE #-} GRSel name f => GRSel name (M1 i s f) where
gSel = gSel @name @f
instance (GRSel name a, GRSel name b) => GRSel name (a :*: b) where
gSel n = gSel @name @a n >>= gSel @name @b . succ
instance GRSel name (K1 i a) where
gSel = Right