module Sqel.Data.Sql where

import Data.Aeson (FromJSON, ToJSON)
import Data.Generics.Labels ()
import qualified Data.Text as Text
import qualified Data.Text.Lazy.Builder as Text
import Exon (
  ExonAppend (exonAppend, exonConcat),
  ExonExpression (exonExpression),
  Result (Empty, Result),
  SkipWs (SkipWs),
  ToSegment (toSegment),
  exonWith,
  skipWs,
  )
import Language.Haskell.TH.Quote (QuasiQuoter)
import Prettyprinter (Pretty (pretty))
import Sqel.Text.Quote (dquote)

newtype Sql = Sql { Sql -> Text
unSql :: Text }
  deriving stock (Sql -> Sql -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Sql -> Sql -> Bool
$c/= :: Sql -> Sql -> Bool
== :: Sql -> Sql -> Bool
$c== :: Sql -> Sql -> Bool
Eq, Int -> Sql -> ShowS
[Sql] -> ShowS
Sql -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Sql] -> ShowS
$cshowList :: [Sql] -> ShowS
show :: Sql -> String
$cshow :: Sql -> String
showsPrec :: Int -> Sql -> ShowS
$cshowsPrec :: Int -> Sql -> ShowS
Show, forall x. Rep Sql x -> Sql
forall x. Sql -> Rep Sql x
forall a.
(forall x. a -> Rep a x) -> (forall x. Rep a x -> a) -> Generic a
$cto :: forall x. Rep Sql x -> Sql
$cfrom :: forall x. Sql -> Rep Sql x
Generic, Eq Sql
Sql -> Sql -> Bool
Sql -> Sql -> Ordering
Sql -> Sql -> Sql
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Sql -> Sql -> Sql
$cmin :: Sql -> Sql -> Sql
max :: Sql -> Sql -> Sql
$cmax :: Sql -> Sql -> Sql
>= :: Sql -> Sql -> Bool
$c>= :: Sql -> Sql -> Bool
> :: Sql -> Sql -> Bool
$c> :: Sql -> Sql -> Bool
<= :: Sql -> Sql -> Bool
$c<= :: Sql -> Sql -> Bool
< :: Sql -> Sql -> Bool
$c< :: Sql -> Sql -> Bool
compare :: Sql -> Sql -> Ordering
$ccompare :: Sql -> Sql -> Ordering
Ord)
  deriving newtype (String -> Sql
forall a. (String -> a) -> IsString a
fromString :: String -> Sql
$cfromString :: String -> Sql
IsString, NonEmpty Sql -> Sql
Sql -> Sql -> Sql
forall b. Integral b => b -> Sql -> Sql
forall a.
(a -> a -> a)
-> (NonEmpty a -> a)
-> (forall b. Integral b => b -> a -> a)
-> Semigroup a
stimes :: forall b. Integral b => b -> Sql -> Sql
$cstimes :: forall b. Integral b => b -> Sql -> Sql
sconcat :: NonEmpty Sql -> Sql
$csconcat :: NonEmpty Sql -> Sql
<> :: Sql -> Sql -> Sql
$c<> :: Sql -> Sql -> Sql
Semigroup, Semigroup Sql
Sql
[Sql] -> Sql
Sql -> Sql -> Sql
forall a.
Semigroup a -> a -> (a -> a -> a) -> ([a] -> a) -> Monoid a
mconcat :: [Sql] -> Sql
$cmconcat :: [Sql] -> Sql
mappend :: Sql -> Sql -> Sql
$cmappend :: Sql -> Sql -> Sql
mempty :: Sql
$cmempty :: Sql
Monoid, [Sql] -> Encoding
[Sql] -> Value
Sql -> Encoding
Sql -> Value
forall a.
(a -> Value)
-> (a -> Encoding)
-> ([a] -> Value)
-> ([a] -> Encoding)
-> ToJSON a
toEncodingList :: [Sql] -> Encoding
$ctoEncodingList :: [Sql] -> Encoding
toJSONList :: [Sql] -> Value
$ctoJSONList :: [Sql] -> Value
toEncoding :: Sql -> Encoding
$ctoEncoding :: Sql -> Encoding
toJSON :: Sql -> Value
$ctoJSON :: Sql -> Value
ToJSON, Value -> Parser [Sql]
Value -> Parser Sql
forall a.
(Value -> Parser a) -> (Value -> Parser [a]) -> FromJSON a
parseJSONList :: Value -> Parser [Sql]
$cparseJSONList :: Value -> Parser [Sql]
parseJSON :: Value -> Parser Sql
$cparseJSON :: Value -> Parser Sql
FromJSON)

instance ConvertUtf8 Text bs => ConvertUtf8 Sql bs where
  encodeUtf8 :: Sql -> bs
encodeUtf8 = forall a b. ConvertUtf8 a b => a -> b
encodeUtf8 forall b c a. (b -> c) -> (a -> b) -> a -> c
. Sql -> Text
unSql

  decodeUtf8 :: bs -> Sql
decodeUtf8 = Text -> Sql
Sql forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. ConvertUtf8 a b => b -> a
decodeUtf8

  decodeUtf8Strict :: bs -> Either UnicodeException Sql
decodeUtf8Strict = forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap Text -> Sql
Sql forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a b. ConvertUtf8 a b => b -> Either UnicodeException a
decodeUtf8Strict

instance Pretty Sql where
  pretty :: forall ann. Sql -> Doc ann
pretty (Sql Text
s) = forall a ann. Pretty a => a -> Doc ann
pretty Text
s

sql :: QuasiQuoter
sql :: QuasiQuoter
sql = Maybe (Q Exp, Q Exp) -> Bool -> Bool -> QuasiQuoter
exonWith (forall a. a -> Maybe a
Just ([e|SkipWs|], [e|skipWs|])) Bool
True Bool
False

class ToSql a where
  toSql :: a -> Sql

instance ToSql Sql where
  toSql :: Sql -> Sql
toSql = forall a. a -> a
id

instance {-# incoherent #-} ToSql a => ToSegment a Sql where
  toSegment :: a -> Sql
toSegment = forall a. ToSql a => a -> Sql
toSql

instance ExonExpression (SkipWs Sql) Text builder where
  exonExpression :: (Text -> builder) -> Text -> Result builder
exonExpression Text -> builder
builder Text
expr
    | Text -> Bool
Text.null Text
expr = forall a. Result a
Empty
    | Bool
otherwise = forall a. a -> Result a
Result (Text -> builder
builder Text
expr)

instance ExonAppend (SkipWs Sql) Text.Builder where
  exonConcat :: NonEmpty (Result Builder) -> Result Builder
exonConcat (Result Builder
h :| [Result Builder]
t) =
    forall {a}.
(IsString a, ExonAppend Sql a) =>
Result a -> [Result a] -> Result a
go Result Builder
h [Result Builder]
t
    where
      go :: Result a -> [Result a] -> Result a
go Result a
Empty (Result a
seg : [Result a]
segs) = Result a -> [Result a] -> Result a
go Result a
seg [Result a]
segs
      go Result a
z (Result a
Empty : Result a
Empty : [Result a]
segs) = Result a -> [Result a] -> Result a
go Result a
z (forall a. Result a
Empty forall a. a -> [a] -> [a]
: [Result a]
segs)
      go Result a
z [Item [Result a]
Result a
Empty] = Result a
z
      go Result a
z (Result a
Empty : [Result a]
segs) = Result a -> [Result a] -> Result a
go Result a
z (forall a. a -> Result a
Result a
" " forall a. a -> [a] -> [a]
: [Result a]
segs)
      go (Result a
z) (Result a
seg : [Result a]
segs) = Result a -> [Result a] -> Result a
go (forall result builder.
ExonAppend result builder =>
builder -> builder -> Result builder
exonAppend @Sql a
z a
seg) [Result a]
segs
      go Result a
z [] = Result a
z

sqlQuote :: Text -> Sql
sqlQuote :: Text -> Sql
sqlQuote = Text -> Sql
Sql forall b c a. (b -> c) -> (a -> b) -> a -> c
. forall a. Exon a => a -> a
dquote