{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Database.Persist.Generic
(
GToPersistValue (..)
, GFromPersistValue (..)
, genericToPersistValue
, genericFromPersistValue
) where
import Control.Applicative ((<|>))
import Data.Bifunctor (first)
import Data.Proxy (Proxy (..))
import Data.Text (Text)
import qualified Data.Text as T
import Database.Persist.Sql (PersistValue (PersistText), toPersistValue, fromPersistValue)
import GHC.Generics (Generic (..), D1, C1, M1(..), (:+:)(..), Meta(..), U1(..))
import GHC.TypeLits (KnownSymbol, symbolVal)
genericToPersistValue :: (Generic a, GToPersistValue (Rep a)) => a -> PersistValue
genericToPersistValue = gToPersistValue . from
genericFromPersistValue
:: (Generic a, GFromPersistValue (Rep a))
=> PersistValue
-> Either Text a
genericFromPersistValue v =
first T.pack (to <$> gFromPersistValue v)
class GToPersistValue f where
gToPersistValue :: f a -> PersistValue
instance GToPersistValue a => GToPersistValue (D1 f a) where
gToPersistValue (M1 x) = gToPersistValue x
instance KnownSymbol name => GToPersistValue (C1 ('MetaCons name x y) U1) where
gToPersistValue (M1 _) = PersistText (T.pack name)
where
name = symbolVal (Proxy @ name)
instance (GToPersistValue l, GToPersistValue r) => GToPersistValue (l :+: r) where
gToPersistValue (L1 x) = gToPersistValue x
gToPersistValue (R1 x) = gToPersistValue x
class GFromPersistValue f where
gFromPersistValue :: PersistValue -> Either String (f a)
instance GFromPersistValue a => GFromPersistValue (D1 f a) where
gFromPersistValue x = M1 <$> gFromPersistValue x
instance KnownSymbol name => GFromPersistValue (C1 ('MetaCons name x y) U1) where
gFromPersistValue (PersistText v) =
if T.unpack v == name
then pure (M1 U1)
else Left $ "Parse error: " <> name
where
name = symbolVal (Proxy @ name)
gFromPersistValue _ = Left $ "Invalid Type: " <> name
where
name = symbolVal (Proxy @ name)
instance (GFromPersistValue l, GFromPersistValue r) => GFromPersistValue (l :+: r) where
gFromPersistValue x = l <|> r
where
l = L1 <$> gFromPersistValue x
r = R1 <$> gFromPersistValue x