packages feed

dani-sqlite-0.1.1.0: lib/Sqlite/Query/Types.hs

{-# LANGUAGE BlockArguments #-}

------------------------------------------------------------------------------

------------------------------------------------------------------------------

module Sqlite.Query.Types
  ( Null (..),
    Solo (..),
    Sql (..),
    (:.) (..),
  )
where

import Control.Arrow (first)
import Data.String (IsString (..))
import Data.Text qualified as T
import Data.Tuple (Solo (..))

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

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.Sqlite.Simple
-- >
-- > q :: Query
-- > q = "select ?"
--
-- The underlying type is a 'Text', and literal Haskell strings that
-- contain Unicode characters will be correctly transformed to UTF-8.
newtype Sql = Sql
  { sqlText :: T.Text
  }
  deriving (Eq, Ord)

instance Show Sql where
  show = show . sqlText

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

instance IsString Sql where
  fromString = Sql . T.pack

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

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

-- | A composite type to parse your custom data structures without
-- having to define dummy newtype wrappers every time.
--
--
-- > instance FromRow MyData where ...
--
-- > instance FromRow MyData2 where ...
--
--
-- then I can do the following for free:
--
-- @
-- res <- query' c "..."
-- forM res $ \\(MyData{..} :. MyData2{..}) -> do
--   ....
-- @
data h :. t = h :. t deriving (Eq, Ord, Show, Read)

infixr 3 :.