content: {-# LANGUAGE TemplateHaskell #-}
  
  -- |
  -- Module      : Database.Relational.Export
  -- Copyright   : 2021 Kei Hibino
  -- License     : BSD3
  --
  -- Maintainer  : ex8k.hibino@gmail.com
  -- Stability   : experimental
  -- Portability : unknown
  --
  -- This module defines templates to export SQL string representation to other systems.
  module Database.Relational.Export (
    inlineQuery_,
    inlineUpdate_,
    inlineInsertValue_,
    inlineInsertQuery_,
    inlineDelete_,
    ) where
  
  import qualified Data.Text as T
  import qualified Data.Text.Encoding as T
  import Data.ByteString.Short (ShortByteString, toShort)
  
  import Language.Haskell.TH (Q, Dec, stringE)
  import Language.Haskell.TH.Name.CamelCase (varName, varCamelcaseName)
  import Language.Haskell.TH.Lib.Extra (simpleValD)
  
  import Database.Relational
    (Query, Update, Insert, InsertQuery, Delete,
     untypeQuery, UntypeableNoFetch (untypeNoFetch))
  
  
  inlineSQL_ :: (String -> Q ()) -- ^ action to check SQL string
             -> String           -- ^ SQL String
             -> String           -- ^ Variable name to define as inlined SQL
             -> Q [Dec]          -- ^ Result declarations
  inlineSQL_ check sql declName = do
    check sql
    simpleValD (varName $ varCamelcaseName declName)
      [t| ShortByteString |]
      [| toShort $ T.encodeUtf8 $ T.pack $(stringE sql) |]
      -- IsString instance of ShortByteString type does not handle multi-byte characters.
  
  inlineQuery_ :: (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
               -> Query p a        -- ^ query to inline
               -> String           -- ^ Variable name to define as inlined query
               -> Q [Dec]          -- ^ Result declarations
  inlineQuery_ check q declName = inlineSQL_ check (untypeQuery q) declName
  
  inlineNoFetch_ :: UntypeableNoFetch s
                => (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
                -> s a              -- ^ statement to inline
                -> String           -- ^ Variable name to define as inlined query
                -> Q [Dec]          -- ^ Result declarations
  inlineNoFetch_ check q declName = inlineSQL_ check (untypeNoFetch q) declName
  
  inlineUpdate_ :: (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
                -> Update p         -- ^ statement to inline
                -> String           -- ^ Variable name to define as inlined query
                -> Q [Dec]          -- ^ Result declarations
  inlineUpdate_ = inlineNoFetch_
  
  inlineInsertValue_ :: (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
                     -> Insert p         -- ^ statement to inline
                     -> String           -- ^ Variable name to define as inlined query
                     -> Q [Dec]          -- ^ Result declarations
  inlineInsertValue_ = inlineNoFetch_
  
  inlineInsertQuery_ :: (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
                     -> InsertQuery p    -- ^ statement to inline
                     -> String           -- ^ Variable name to define as inlined query
                     -> Q [Dec]          -- ^ Result declarations
  inlineInsertQuery_ = inlineNoFetch_
  
  inlineDelete_ :: (String -> Q ()) -- ^ action to check SQL string. for example to call prepare. if you do not need this, pass (const $ pure ())
                -> Delete p         -- ^ statement to inline
                -> String           -- ^ Variable name to define as inlined query
                -> Q [Dec]          -- ^ Result declarations
  inlineDelete_ = inlineNoFetch_
  