module Hasql.Interpolate
  ( -- * QuasiQuoters
    sql,
    Sql,

    -- * Interpolators
    interp,
    interpFoldl,
    interpWith,

    -- * Decoders
    DecodeValue (..),
    DecodeField (..),
    DecodeRow (..),
    DecodeResult (..),

    -- * Encoders
    EncodeValue (..),
    EncodeField,

    -- * Newtypes for decoding/encoding
    OneRow (..),
    OneColumn (..),
    RowsAffected (..),
    Json (..),
    Jsonb (..),
    AsJson (..),
    AsJsonb (..),
    CompositeValue (..),

    -- * toTable
    toTable,
    EncodeRow (..),
  )
where

import Control.Monad.Trans.State.Strict (evalState)
import Data.ByteString.Builder (toLazyByteString)
import Data.ByteString.Lazy (toStrict)
import Hasql.Decoders (Result, foldlRows)
import Hasql.Interpolate.Internal.CompositeValue
import Hasql.Interpolate.Internal.Decoder
import Hasql.Interpolate.Internal.EncodeRow
import Hasql.Interpolate.Internal.Encoder
import Hasql.Interpolate.Internal.Json
import Hasql.Interpolate.Internal.OneColumn
import Hasql.Interpolate.Internal.OneRow
import Hasql.Interpolate.Internal.RowsAffected
import Hasql.Interpolate.Internal.Sql
import Hasql.Interpolate.Internal.TH
import Hasql.Statement (Statement (..))

-- | Interpolate a 'Sql' into a 'Statement' using the 'DecodeResult'
-- type class to determine the appropriate decoder.
--
-- @
-- example :: Int64 -> Statement () [(Int64, Int64)]
-- example bonk = interp False [sql| select x, y from t where t.x > #{bonk} |]
-- @
interp ::
  DecodeResult b =>
  -- | 'True' if the 'Statement' should be prepared
  Bool ->
  Sql ->
  Statement () b
interp :: Bool -> Sql -> Statement () b
interp Bool
prepared = Bool -> Result b -> Sql -> Statement () b
forall b. Bool -> Result b -> Sql -> Statement () b
interpWith Bool
prepared Result b
forall a. DecodeResult a => Result a
decodeResult

-- | interpolate then consume with 'foldlRows'
interpFoldl :: DecodeRow a => Bool -> (b -> a -> b) -> b -> Sql -> Statement () b
interpFoldl :: Bool -> (b -> a -> b) -> b -> Sql -> Statement () b
interpFoldl Bool
prepared b -> a -> b
f b
z = Bool -> Result b -> Sql -> Statement () b
forall b. Bool -> Result b -> Sql -> Statement () b
interpWith Bool
prepared ((b -> a -> b) -> b -> Row a -> Result b
forall a b. (a -> b -> a) -> a -> Row b -> Result a
foldlRows b -> a -> b
f b
z Row a
forall a. DecodeRow a => Row a
decodeRow)

-- | A more general version of 'interp' that allows for passing an
-- explicit decoder.
interpWith :: Bool -> Result b -> Sql -> Statement () b
interpWith :: Bool -> Result b -> Sql -> Statement () b
interpWith Bool
prepare Result b
decoder (Sql State Int Builder
bldr Params ()
enc) = ByteString -> Params () -> Result b -> Bool -> Statement () b
forall a b.
ByteString -> Params a -> Result b -> Bool -> Statement a b
Statement (ByteString -> ByteString
toStrict (Builder -> ByteString
toLazyByteString (State Int Builder -> Int -> Builder
forall s a. State s a -> s -> a
evalState State Int Builder
bldr Int
1))) Params ()
enc Result b
decoder Bool
prepare