packages feed

hasql-interpolate-0.1.0.0: lib/Hasql/Interpolate/Internal/Decoder/TH.hs

{-# LANGUAGE TemplateHaskell #-}

module Hasql.Interpolate.Internal.Decoder.TH
  ( genDecodeRowInstance,
  )
where

import Control.Monad
import Data.Foldable (foldl')
import Hasql.Decoders
import Language.Haskell.TH

-- | Generate a single 'Hasql.Interpolate.DecodeRow' instance for a
-- tuple of size @tupSize@
genDecodeRowInstance ::
  -- | tuple size
  Int ->
  Q Dec
genDecodeRowInstance tupSize
  | tupSize < 2 = fail "this is just for tuples, must specify a tuple size of 2 or greater"
  | otherwise = do
    tyVars <- replicateM tupSize (newName "x")
    context <- traverse (\x -> [t|$(conT (mkName "DecodeField")) $(varT x)|]) tyVars
    instanceHead <- [t|$(conT (mkName "DecodeRow")) $(pure $ foldl' AppT (TupleT tupSize) (map VarT tyVars))|]
    let tupSection = TupE (replicate tupSize Nothing)
        go b _a = do
          [e|$(b) <*> column decodeField|]

    instanceBodyExp <- foldl' go [e|$(pure tupSection) <$> column decodeField|] (tail tyVars)
    let instanceBody = FunD (mkName "decodeRow") [Clause [] (NormalB instanceBodyExp) []]
    pure (InstanceD Nothing context instanceHead [instanceBody])