{-# LANGUAGE DeriveDataTypeable, DeriveFunctor, GeneralizedNewtypeDeriving #-}

-- |
-- Module:      Database.MySQL.Simple.Types
-- Copyright:   (c) 2011 MailRank, Inc.
-- License:     BSD3
-- Maintainer:  Paul Rouse <pyr@doynton.org>
-- Stability:   experimental
-- Portability: portable
--
-- Basic types.

module Database.MySQL.Simple.Types
    (
      Null(..)
    , Only(..)
    , In(..)
    , VaArgs(..)
    , Binary(..)
    , Query(..)
    ) where

import Blaze.ByteString.Builder (toByteString)
import Control.Arrow (first)
import Data.ByteString (ByteString)
import Data.Semigroup (Semigroup(..))
import Data.Monoid (Monoid(..))
import Data.String (IsString(..))
import Data.Typeable (Typeable)
import qualified Blaze.ByteString.Builder.Char.Utf8 as Utf8
import qualified Data.ByteString as B

-- | A placeholder for the SQL @NULL@ value.
data Null = Null
          deriving (Read, Show, Typeable)

instance Eq Null where
    _ == _ = False
    _ /= _ = False

-- | A query string. This type is intended to make it difficult to
-- construct a SQL query by concatenating string fragments, as that is
-- an extremely common way to accidentally introduce SQL injection
-- vulnerabilities into an application.
--
-- This type is an instance of 'IsString', so the easiest way to
-- construct a query is to enable the @OverloadedStrings@ language
-- extension and then simply write the query in double quotes.
--
-- > {-# LANGUAGE OverloadedStrings #-}
-- >
-- > import Database.MySQL.Simple
-- >
-- > q :: Query
-- > q = "select ?"
--
-- The underlying type is a 'ByteString', and literal Haskell strings
-- that contain Unicode characters will be correctly transformed to
-- UTF-8.
newtype Query = Query {
      fromQuery :: ByteString
    } deriving (Eq, Ord, Typeable)

instance Show Query where
    show = show . fromQuery

instance Read Query where
    readsPrec i = fmap (first Query) . readsPrec i

instance IsString Query where
    fromString = Query . toByteString . Utf8.fromString

instance Semigroup Query where
    (<>) (Query a) (Query b) = Query (B.append a b)
    {-# INLINE (<>) #-}

instance Monoid Query where
    mempty = Query B.empty
    mappend = (<>)
    {-# INLINE mappend #-}

-- | A single-value \"collection\".
--
-- This is useful if you need to supply a single parameter to a SQL
-- query, or extract a single column from a SQL result.
--
-- Parameter example:
--
-- @query c \"select x from scores where x > ?\" ('Only' (42::Int))@
--
-- Result example:
--
-- @xs <- query_ c \"select id from users\"
--forM_ xs $ \\('Only' id) -> {- ... -}@
newtype Only a = Only {
      fromOnly :: a
    } deriving (Eq, Ord, Read, Show, Typeable, Functor)

-- | Wrap a list of values for use in an @IN@ clause.  Replaces a
-- single \"@?@\" character with a parenthesized list of rendered
-- values.
--
-- Example:
--
-- > query c "select * from whatever where id in ?" (Only (In [3,4,5]))
newtype In a = In a
    deriving (Eq, Ord, Read, Show, Typeable, Functor)

-- | Wrap a list of values for use in a function with variable arguments.
-- Replaces a single \"@?@\" character with a non-parenthesized list of
-- rendered values.
--
-- Example:
--
-- > query conn
-- >   "SELECT * FROM example_table ORDER BY field(f,?)"
-- >   (Only (VaArgs [3,2,1]))
newtype VaArgs a = VaArgs a
  deriving (Eq, Ord, Read, Show, Typeable, Functor)

-- | Wrap a mostly-binary string to be escaped in hexadecimal.
newtype Binary a = Binary a
    deriving (Eq, Ord, Read, Show, Typeable, Functor)
