module ClickHaskell.Primitive.TString where

-- Internal
import ClickHaskell.Primitive.Serialization

-- GHC included
import Control.DeepSeq (NFData)
import Data.Binary.Get
import Data.ByteString as BS (ByteString, length)
import Data.ByteString.Builder
import Data.ByteString.Char8 as BS8 (pack, unpack, concatMap, singleton)
import Data.ByteString.Lazy (toStrict)
import Data.String (IsString (..))
import Prelude hiding (liftA2)



-- ** ChString

{- | ClickHouse String column type -}
newtype ChString = MkChString BS.ByteString
  deriving newtype (Int -> ChString -> ShowS
[ChString] -> ShowS
ChString -> String
(Int -> ChString -> ShowS)
-> (ChString -> String) -> ([ChString] -> ShowS) -> Show ChString
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ChString -> ShowS
showsPrec :: Int -> ChString -> ShowS
$cshow :: ChString -> String
show :: ChString -> String
$cshowList :: [ChString] -> ShowS
showList :: [ChString] -> ShowS
Show, ChString -> ChString -> Bool
(ChString -> ChString -> Bool)
-> (ChString -> ChString -> Bool) -> Eq ChString
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ChString -> ChString -> Bool
== :: ChString -> ChString -> Bool
$c/= :: ChString -> ChString -> Bool
/= :: ChString -> ChString -> Bool
Eq, String -> ChString
(String -> ChString) -> IsString ChString
forall a. (String -> a) -> IsString a
$cfromString :: String -> ChString
fromString :: String -> ChString
IsString, ChString -> ()
(ChString -> ()) -> NFData ChString
forall a. (a -> ()) -> NFData a
$crnf :: ChString -> ()
rnf :: ChString -> ()
NFData)

instance IsChType ChString where
  chTypeName :: String
chTypeName = String
"String"
  defaultValueOfTypeName :: ChString
defaultValueOfTypeName = ChString
""

instance Serializable ChString where
  serialize :: ProtocolRevision -> ChString -> Builder
serialize ProtocolRevision
rev (MkChString ByteString
str) = (forall chType.
Serializable chType =>
ProtocolRevision -> chType -> Builder
serialize @UVarInt ProtocolRevision
rev (UVarInt -> Builder)
-> (ByteString -> UVarInt) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Int -> UVarInt
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Int -> UVarInt) -> (ByteString -> Int) -> ByteString -> UVarInt
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Int
BS.length) ByteString
str Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
byteString ByteString
str
  deserialize :: ProtocolRevision -> Get ChString
deserialize ProtocolRevision
rev = do
    len <- forall chType.
Serializable chType =>
ProtocolRevision -> Get chType
deserialize @UVarInt ProtocolRevision
rev
    MkChString <$> (getByteString . fromIntegral) len
  {-# INLINE deserialize #-}

instance ToChType ChString BS.ByteString where
  toChType :: ByteString -> ChString
toChType = ByteString -> ChString
MkChString
  fromChType :: ChString -> ByteString
fromChType (MkChString ByteString
string) = ByteString
string

instance ToChType ChString Builder where
  toChType :: Builder -> ChString
toChType = ByteString -> ChString
MkChString (ByteString -> ChString)
-> (Builder -> ByteString) -> Builder -> ChString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. LazyByteString -> ByteString
toStrict (LazyByteString -> ByteString)
-> (Builder -> LazyByteString) -> Builder -> ByteString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Builder -> LazyByteString
toLazyByteString
  fromChType :: ChString -> Builder
fromChType (MkChString ByteString
string) = ByteString -> Builder
byteString ByteString
string

instance ToChType ChString String where
  toChType :: String -> ChString
toChType = ByteString -> ChString
MkChString (ByteString -> ChString)
-> (String -> ByteString) -> String -> ChString
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> ByteString
BS8.pack
  fromChType :: ChString -> String
fromChType (MkChString ByteString
bs)= ByteString -> String
BS8.unpack ByteString
bs

instance ToQueryPart ChString where
  toQueryPart :: ChString -> Builder
toQueryPart (MkChString ByteString
string) =  Builder
"'" Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> ByteString -> Builder
escapeQuery ByteString
string Builder -> Builder -> Builder
forall a. Semigroup a => a -> a -> a
<> Builder
"'"
    where
    escapeQuery :: BS.ByteString -> Builder
    escapeQuery :: ByteString -> Builder
escapeQuery = ByteString -> Builder
byteString (ByteString -> Builder)
-> (ByteString -> ByteString) -> ByteString -> Builder
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Char -> ByteString) -> ByteString -> ByteString
BS8.concatMap (\Char
sym ->
      case Char
sym of
        Char
'\'' -> ByteString
"\\\'"
        Char
'\\' -> ByteString
"\\\\"
        Char
_ -> Char -> ByteString
BS8.singleton Char
sym
      )