{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DefaultSignatures #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
module Database.GP.Entity
( Entity (..),
columnNameFor,
gtoRow,
GToRow,
GFromRow,
maybeFieldTypeFor,
Conn(..),
TxHandling(..),
maybeIdFieldIndex,
fieldIndex,
)
where
import Data.Char (toLower)
import Data.Convertible
import Data.Kind
import Data.Typeable (Proxy (..), TypeRep)
import Database.GP.TypeInfo
import Database.HDBC (SqlValue)
import GHC.Generics
import GHC.TypeNats
import Database.GP.Conn
import Data.List (elemIndex)
class (Generic a, HasConstructor (Rep a), HasSelectors (Rep a)) => Entity a where
fromRow :: Conn -> [SqlValue] -> IO a
toRow :: Conn -> a -> IO [SqlValue]
idField :: String
fieldsToColumns :: [(String, String)]
tableName :: String
autoIncrement :: Bool
default fromRow :: (GFromRow (Rep a)) => Conn -> [SqlValue] -> IO a
fromRow Conn
_conn = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a x. Generic a => Rep a x -> a
to forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {k} (f :: k -> *) (a :: k). GFromRow f => [SqlValue] -> f a
gfromRow
default toRow :: GToRow (Rep a) => Conn -> a -> IO [SqlValue]
toRow Conn
_ = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall {k} (f :: k -> *) (a :: k). GToRow f => f a -> [SqlValue]
gtoRow forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a x. Generic a => a -> Rep a x
from
default idField :: String
idField = forall a b. (a -> b) -> [a] -> [b]
map Char -> Char
toLower (forall {k} (a :: k). TypeInfo a -> String
constructorName (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a)) forall a. [a] -> [a] -> [a]
++ String
"ID"
default fieldsToColumns :: [(String, String)]
fieldsToColumns = forall a b. [a] -> [b] -> [(a, b)]
zip (forall {k} (a :: k). TypeInfo a -> [String]
fieldNames (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a)) (forall {k} (a :: k). TypeInfo a -> [String]
fieldNames (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a))
default tableName :: String
tableName = forall {k} (a :: k). TypeInfo a -> String
constructorName (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a)
default autoIncrement :: Bool
autoIncrement = Bool
True
maybeIdFieldIndex :: forall a. (Entity a) => Maybe Int
maybeIdFieldIndex :: forall a. Entity a => Maybe Int
maybeIdFieldIndex = forall a. Eq a => a -> [a] -> Maybe Int
elemIndex (forall a. Entity a => String
idField @a) (forall {k} (a :: k). TypeInfo a -> [String]
fieldNames (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a))
fieldIndex :: forall a. (Entity a) => String -> Int
fieldIndex :: forall a. Entity a => String -> Int
fieldIndex String
fieldName =
forall a. String -> Maybe a -> a
expectJust
(String
"Field " forall a. [a] -> [a] -> [a]
++ String
fieldName forall a. [a] -> [a] -> [a]
++ String
" is not present in type " forall a. [a] -> [a] -> [a]
++ forall {k} (a :: k). TypeInfo a -> String
constructorName TypeInfo a
ti)
(forall a. Eq a => a -> [a] -> Maybe Int
elemIndex String
fieldName [String]
fieldList)
where
ti :: TypeInfo a
ti = forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a
fieldList :: [String]
fieldList = forall {k} (a :: k). TypeInfo a -> [String]
fieldNames TypeInfo a
ti
expectJust :: String -> Maybe a -> a
expectJust :: forall a. String -> Maybe a -> a
expectJust String
_ (Just a
x) = a
x
expectJust String
err Maybe a
Nothing = forall a. HasCallStack => String -> a
error (String
"expectJust " forall a. [a] -> [a] -> [a]
++ String
err)
columnNameFor :: forall a. (Entity a) => String -> String
columnNameFor :: forall a. Entity a => String -> String
columnNameFor String
fieldName =
case String -> Maybe String
maybeColumnNameFor String
fieldName of
Just String
columnName -> String
columnName
Maybe String
Nothing ->
forall a. HasCallStack => String -> a
error
( String
"columnNameFor: "
forall a. [a] -> [a] -> [a]
++ forall a. Entity a => String
tableName @a
forall a. [a] -> [a] -> [a]
++ String
" has no column mapping for "
forall a. [a] -> [a] -> [a]
++ String
fieldName
)
where
maybeColumnNameFor :: String -> Maybe String
maybeColumnNameFor :: String -> Maybe String
maybeColumnNameFor String
field = forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
field (forall a. Entity a => [(String, String)]
fieldsToColumns @a)
maybeFieldTypeFor :: forall a. (Entity a) => String -> Maybe TypeRep
maybeFieldTypeFor :: forall a. Entity a => String -> Maybe TypeRep
maybeFieldTypeFor String
field = forall a b. Eq a => a -> [(a, b)] -> Maybe b
lookup String
field (TypeInfo a -> [(String, TypeRep)]
fieldsAndTypes (forall a.
(HasConstructor (Rep a), HasSelectors (Rep a), Generic a) =>
TypeInfo a
typeInfo @a))
where
fieldsAndTypes :: TypeInfo a -> [(String, TypeRep)]
fieldsAndTypes :: TypeInfo a -> [(String, TypeRep)]
fieldsAndTypes TypeInfo a
ti = forall a b. [a] -> [b] -> [(a, b)]
zip (forall {k} (a :: k). TypeInfo a -> [String]
fieldNames TypeInfo a
ti) (forall {k} (a :: k). TypeInfo a -> [TypeRep]
fieldTypes TypeInfo a
ti)
class GToRow f where
gtoRow :: f a -> [SqlValue]
instance (Convertible a SqlValue) => GToRow (K1 i a) where
gtoRow :: forall (a :: k). K1 i a a -> [SqlValue]
gtoRow (K1 a
a) = forall (f :: * -> *) a. Applicative f => a -> f a
pure forall a b. (a -> b) -> a -> b
$ forall a b. Convertible a b => a -> b
convert a
a
instance (GToRow a, GToRow b) => GToRow (a :*: b) where
gtoRow :: forall (a :: k). (:*:) a b a -> [SqlValue]
gtoRow (a a
a :*: b a
b) = forall {k} (f :: k -> *) (a :: k). GToRow f => f a -> [SqlValue]
gtoRow a a
a forall a. Monoid a => a -> a -> a
`mappend` forall {k} (f :: k -> *) (a :: k). GToRow f => f a -> [SqlValue]
gtoRow b a
b
instance GToRow a => GToRow (M1 i c a) where
gtoRow :: forall (a :: k). M1 i c a a -> [SqlValue]
gtoRow (M1 a a
a) = forall {k} (f :: k -> *) (a :: k). GToRow f => f a -> [SqlValue]
gtoRow a a
a
class GFromRow f where
gfromRow :: [SqlValue] -> f a
instance (Convertible SqlValue a) => GFromRow (K1 i a) where
gfromRow :: forall (a :: k). [SqlValue] -> K1 i a a
gfromRow = forall k i c (p :: k). c -> K1 i c p
K1 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall a b. Convertible a b => a -> b
convert forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. [a] -> a
head
instance GFromRow a => GFromRow (M1 i c a) where
gfromRow :: forall (a :: k). [SqlValue] -> M1 i c a a
gfromRow = forall k i (c :: Meta) (f :: k -> *) (p :: k). f p -> M1 i c f p
M1 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall {k} (f :: k -> *) (a :: k). GFromRow f => [SqlValue] -> f a
gfromRow
instance (KnownNat (NumFields f), GFromRow f, GFromRow g) => GFromRow (f :*: g) where
gfromRow :: forall a. [SqlValue] -> (:*:) f g a
gfromRow [SqlValue]
row = forall {k} (f :: k -> *) (a :: k). GFromRow f => [SqlValue] -> f a
gfromRow [SqlValue]
rowf forall k (f :: k -> *) (g :: k -> *) (p :: k).
f p -> g p -> (:*:) f g p
:*: forall {k} (f :: k -> *) (a :: k). GFromRow f => [SqlValue] -> f a
gfromRow [SqlValue]
rowg
where
([SqlValue]
rowf, [SqlValue]
rowg) = forall a. Int -> [a] -> ([a], [a])
splitAt Int
fNumFields [SqlValue]
row
fNumFields :: Int
fNumFields = forall a b. (Integral a, Num b) => a -> b
fromIntegral (forall (n :: Natural) (proxy :: Natural -> *).
KnownNat n =>
proxy n -> Natural
natVal (forall {k} (t :: k). Proxy t
Proxy :: Proxy (NumFields f)))
type family NumFields (f :: Type -> Type) :: Nat where
NumFields (M1 i c f) = 1
NumFields (f :*: g) = NumFields f + NumFields g