{-# LANGUAGE PatternSynonyms, PatternGuards, TemplateHaskell, GADTs, KindSignatures, DataKinds #-} module Database.PostgreSQL.Typed.Internal ( stringE , pattern StringE , SQLSplit(..) , sqlSplitExprs , sqlSplitParams ) where import Data.Char (isDigit) import Data.String (IsString(..)) import qualified Language.Haskell.TH as TH import Numeric (readDec) stringE :: String -> TH.Exp stringE = TH.LitE . TH.StringL pattern StringE s = TH.LitE (TH.StringL s) instance IsString TH.Exp where fromString = stringE data SQLSplit a (literal :: Bool) where SQLLiteral :: String -> SQLSplit a 'False -> SQLSplit a 'True SQLPlaceholder :: a -> SQLSplit a 'True -> SQLSplit a 'False SQLSplitEnd :: SQLSplit a any sqlCons :: Char -> SQLSplit a 'True -> SQLSplit a 'True sqlCons c (SQLLiteral s l) = SQLLiteral (c : s) l sqlCons c SQLSplitEnd = SQLLiteral [c] SQLSplitEnd sqlSplitExprs :: String -> SQLSplit String 'True sqlSplitExprs ('$':'$':'{':s) = sqlCons '$' $ sqlCons '{' $ sqlSplitExprs s sqlSplitExprs ('$':'{':s) | (e, '}':r) <- break (\c -> c == '{' || c == '}') s = SQLLiteral "" $ SQLPlaceholder e $ sqlSplitExprs r | otherwise = error $ "Error parsing SQL: could not find end of expression: ${" ++ s sqlSplitExprs (c:s) = sqlCons c $ sqlSplitExprs s sqlSplitExprs [] = SQLSplitEnd sqlSplitParams :: String -> SQLSplit Int 'True sqlSplitParams ('$':'$':d:s) | isDigit d = sqlCons '$' $ sqlCons d $ sqlSplitParams s sqlSplitParams ('$':s@(d:_)) | isDigit d, [(n, r)] <- readDec s = SQLLiteral "" $ SQLPlaceholder n $ sqlSplitParams r sqlSplitParams (c:s) = sqlCons c $ sqlSplitParams s sqlSplitParams [] = SQLSplitEnd