packages feed

beam-large-records-0.1.1: src/Data/Record/Beam/FromBackendRow.hs

{-# LANGUAGE ConstraintKinds       #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE MonoLocalBinds        #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeApplications      #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE UndecidableInstances  #-}

module Data.Record.Beam.FromBackendRow (
    GFromLargeBackendRow
  , FromBackendRowI
  ) where

import Data.Functor.Identity
import Data.List (foldl')
import Data.Proxy
import Data.Record.Generic
import Data.Record.Generic.GHC
import Data.Record.Generic.Transform
import Database.Beam.Backend.SQL.Row
import Database.Beam.Schema.Tables
import GHC.Generics hiding (Generic(..), (:.:))

import qualified Data.Record.Generic.Rep as Rep

import Data.Record.Beam.Interpretation

type GFromLargeBackendRow be tbl = (
    Generic (tbl Identity)
  , Generic (tbl Uninterpreted)
  , HasNormalForm (BeamInterpretation Identity) (tbl Identity) (tbl Uninterpreted)
  , Constraints (tbl Uninterpreted) (FromBackendRowI be)
  )

instance GFromLargeBackendRow be tbl
      => GFromBackendRow be (ThroughLRGenerics (tbl Exposed))
                            (ThroughLRGenerics (tbl Identity)) where
  gFromBackendRow _ =
      fmap (WrapThroughLRGenerics . to . denormalize1 (Proxy @BeamInterpretation)) $
        Rep.sequenceA perField
    where
      perField :: Rep (FromBackendRowM be :.: Interpret (BeamInterpretation Identity)) (tbl Uninterpreted)
      perField = Rep.cpure (Proxy @(FromBackendRowI be)) (Comp fromBackendRowI)

  gValuesNeeded pBackend _ _ =
       foldl' (+) 0 $ Rep.collapse perField
   where
      perField :: Rep (K Int) (tbl Uninterpreted)
      perField = Rep.cpure (Proxy @(FromBackendRowI be)) (valuesNeededI pBackend)

{-------------------------------------------------------------------------------
  Internal

  NOTE: the superclass constraints on the 'FromBackendRowI' instances match
  instance heads in beam, but his is what is used in the definition of
  'GFromBackendRow' itself, so we stick with it (and use @MonoLocalBinds@).
-------------------------------------------------------------------------------}

class FromBackendRowI be x where
  fromBackendRowI :: FromBackendRowM be (Interpret (BeamInterpretation Identity) x)
  valuesNeededI   :: Proxy be -> K Int x

instance FromBackendRow be x => FromBackendRowI be (Uninterpreted x) where
  fromBackendRowI = Interpret . unK1 <$> fromBeam
    where
      fromBeam :: FromBackendRowM be (K1 R x ())
      fromBeam = gFromBackendRow (Proxy @(K1 R (Exposed x)))

  valuesNeededI pBackend = K $
      gValuesNeeded
        pBackend
        (Proxy @(K1 R (Exposed x)))
        (Proxy @((K1 R x)))

instance FromBackendRow be (tbl Identity) => FromBackendRowI be (tbl Uninterpreted) where
  fromBackendRowI = Interpret . unK1 <$> fromBeam
    where
      fromBeam :: FromBackendRowM be (K1 R (tbl Identity) ())
      fromBeam = gFromBackendRow (Proxy @(K1 R (tbl Exposed)))

  valuesNeededI pBackend = K $
      gValuesNeeded
        pBackend
        (Proxy @(K1 R (tbl Exposed)))
        (Proxy @(K1 R (tbl Identity)))

instance FromBackendRow  be (tbl (Nullable Identity))
      => FromBackendRowI be (tbl (Nullable Uninterpreted)) where
  fromBackendRowI = Interpret . unK1 <$> fromBeam
    where
      fromBeam :: FromBackendRowM be (K1 R (tbl (Nullable Identity)) ())
      fromBeam = gFromBackendRow (Proxy @(K1 R (tbl (Nullable Exposed))))

  valuesNeededI pBackend = K $
      gValuesNeeded
        pBackend
        (Proxy @(K1 R (tbl (Nullable Exposed))))
        (Proxy @(K1 R (tbl (Nullable Identity))))