{-# LANGUAGE TemplateHaskell #-}
module Database.PostgreSQL.Simple.SqlQQ.Interpolated
( isql
, quoteInterpolatedSql
, iquery
, iexecute
, iexecute_
) where
import Language.Haskell.TH (Exp, Q, appE, listE, sigE, tupE, varE)
import Language.Haskell.TH.Quote (QuasiQuoter (..))
import Database.PostgreSQL.Simple.ToField (Action, toField)
import Database.PostgreSQL.Simple.SqlQQ (sql)
import Text.Parsec (ParseError)
import Database.PostgreSQL.Simple
import Database.PostgreSQL.Simple.SqlQQ.Interpolated.Parser (StringPart (..), parseInterpolated)
isql :: QuasiQuoter
isql :: QuasiQuoter
isql = QuasiQuoter :: (String -> Q Exp)
-> (String -> Q Pat)
-> (String -> Q Type)
-> (String -> Q [Dec])
-> QuasiQuoter
QuasiQuoter
{ quoteExp :: String -> Q Exp
quoteExp = String -> Q Exp
quoteInterpolatedSql
, quotePat :: String -> Q Pat
quotePat = String -> String -> Q Pat
forall a. HasCallStack => String -> a
error String
"isql quasiquoter does not support usage in patterns"
, quoteType :: String -> Q Type
quoteType = String -> String -> Q Type
forall a. HasCallStack => String -> a
error String
"isql quasiquoter does not support usage in types"
, quoteDec :: String -> Q [Dec]
quoteDec = String -> String -> Q [Dec]
forall a. HasCallStack => String -> a
error String
"isql quasiquoter does not support usage in declarations"
}
combineParts :: [StringPart] -> (String, [Q Exp])
combineParts :: [StringPart] -> (String, [Q Exp])
combineParts = (StringPart -> (String, [Q Exp]) -> (String, [Q Exp]))
-> (String, [Q Exp]) -> [StringPart] -> (String, [Q Exp])
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr StringPart -> (String, [Q Exp]) -> (String, [Q Exp])
step (String
"", [])
where
step :: StringPart -> (String, [Q Exp]) -> (String, [Q Exp])
step StringPart
subExpr (String
s, [Q Exp]
exprs) = case StringPart
subExpr of
Lit String
str -> (String
str String -> String -> String
forall a. Semigroup a => a -> a -> a
<> String
s, [Q Exp]
exprs)
Esc Char
c -> (Char
c Char -> String -> String
forall a. a -> [a] -> [a]
: String
s, [Q Exp]
exprs)
Anti Q Exp
e -> (Char
'?' Char -> String -> String
forall a. a -> [a] -> [a]
: String
s, Q Exp
e Q Exp -> [Q Exp] -> [Q Exp]
forall a. a -> [a] -> [a]
: [Q Exp]
exprs)
applySql :: [StringPart] -> Q Exp
applySql :: [StringPart] -> Q Exp
applySql [StringPart]
parts =
let
(String
s', [Q Exp]
exps) = [StringPart] -> (String, [Q Exp])
combineParts [StringPart]
parts
in
[Q Exp] -> Q Exp
tupE [QuasiQuoter -> String -> Q Exp
quoteExp QuasiQuoter
sql String
s', Q Exp -> Q Type -> Q Exp
sigE ([Q Exp] -> Q Exp
listE ([Q Exp] -> Q Exp) -> [Q Exp] -> Q Exp
forall a b. (a -> b) -> a -> b
$ (Q Exp -> Q Exp) -> [Q Exp] -> [Q Exp]
forall a b. (a -> b) -> [a] -> [b]
map (Q Exp -> Q Exp -> Q Exp
appE (Name -> Q Exp
varE 'toField)) [Q Exp]
exps) [t| [Action] |]]
quoteInterpolatedSql :: String -> Q Exp
quoteInterpolatedSql :: String -> Q Exp
quoteInterpolatedSql String
s = (ParseError -> Q Exp)
-> ([StringPart] -> Q Exp)
-> Either ParseError [StringPart]
-> Q Exp
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (String -> ParseError -> Q Exp
handleError String
s) [StringPart] -> Q Exp
applySql (String -> Either ParseError [StringPart]
parseInterpolated String
s)
handleError :: String -> ParseError -> Q Exp
handleError :: String -> ParseError -> Q Exp
handleError String
expStr ParseError
parseError = String -> Q Exp
forall a. HasCallStack => String -> a
error (String -> Q Exp) -> String -> Q Exp
forall a b. (a -> b) -> a -> b
$ [String] -> String
forall a. Monoid a => [a] -> a
mconcat
[ String
"Failed to parse interpolated expression in string: "
, String
expStr
, String
"\n"
, ParseError -> String
forall a. Show a => a -> String
show ParseError
parseError
]
iquery :: QuasiQuoter
iquery :: QuasiQuoter
iquery = QuasiQuoter
isql { quoteExp :: String -> Q Exp
quoteExp = Q Exp -> Q Exp -> Q Exp
appE [| uncurry query |] (Q Exp -> Q Exp) -> (String -> Q Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Q Exp
quoteInterpolatedSql }
iexecute :: QuasiQuoter
iexecute :: QuasiQuoter
iexecute = QuasiQuoter
isql { quoteExp :: String -> Q Exp
quoteExp = Q Exp -> Q Exp -> Q Exp
appE [| uncurry execute |] (Q Exp -> Q Exp) -> (String -> Q Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Q Exp
quoteInterpolatedSql }
iexecute_ :: QuasiQuoter
iexecute_ :: QuasiQuoter
iexecute_ = QuasiQuoter
isql { quoteExp :: String -> Q Exp
quoteExp = Q Exp -> Q Exp -> Q Exp
appE [| uncurry execute_ |] (Q Exp -> Q Exp) -> (String -> Q Exp) -> String -> Q Exp
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Q Exp
quoteInterpolatedSql }