content: {-# OPTIONS_GHC -fno-warn-unused-binds #-}
  {-# LANGUAGE GADTs, TypeOperators, TypeFamilies, FlexibleInstances #-}
  {-# LANGUAGE GeneralizedNewtypeDeriving, MultiParamTypeClasses #-}
  {-# LANGUAGE UndecidableInstances, DeriveGeneric, OverloadedStrings #-}
  {-# LANGUAGE CPP #-}
  -- | Basic Selda types.
  module Database.Selda.Types
    ( (:*:)(..), Head, Tup (..)
    , first, second, third, fourth, fifth
    , ColName, TableName
    , modColName, mkColName, mkTableName, addColSuffix, addColPrefix
    , fromColName, fromTableName, rawTableName, intercalateColNames
    ) where
  import Data.Dynamic ( Typeable )
  import Data.String ( IsString )
  import Data.Text (Text, replace, append, intercalate)
  import GHC.Generics (Generic)
  
  -- | Name of a database column.
  newtype ColName = ColName { unColName :: Text }
    deriving (Ord, Eq, Show, IsString)
  
  -- | Name of a database table.
  newtype TableName = TableName Text
    deriving (Ord, Eq, Show, IsString)
  
  -- | Modify the given column name using the given function.
  modColName :: ColName -> (Text -> Text) -> ColName
  modColName (ColName cn) f = ColName (f cn)
  
  -- | Add a prefix to a column name.
  addColPrefix :: ColName -> Text -> ColName
  addColPrefix (ColName cn) s = ColName $ Data.Text.append s cn
  
  -- | Add a suffix to a column name.
  addColSuffix :: ColName -> Text -> ColName
  addColSuffix (ColName cn) s = ColName $ Data.Text.append cn s
  
  -- | Convert a column name into a string, with quotes.
  fromColName :: ColName -> Text
  fromColName (ColName cn) = mconcat ["\"", escapeQuotes cn, "\""]
  
  -- | Convert column names into a string, without quotes, intercalating the given
  -- string.
  --
  -- @
  -- intercalateColNames "_" [ColName "a", ColName "b"] == "a_b"
  -- @
  intercalateColNames :: Text -> [ColName] -> Text
  intercalateColNames inter cs = intercalate inter (escapeQuotes . unColName <$> cs)
  
  -- | Convert a table name into a string, with quotes.
  fromTableName :: TableName -> Text
  fromTableName (TableName tn) = mconcat ["\"", escapeQuotes tn, "\""]
  
  -- | Convert a table name into a string, without quotes.
  rawTableName :: TableName -> Text
  rawTableName (TableName tn) = escapeQuotes tn
  
  -- | Create a column name.
  mkColName :: Text -> ColName
  mkColName = ColName
  
  -- | Create a column name.
  mkTableName :: Text -> TableName
  mkTableName = TableName
  
  -- | Escape double quotes in an SQL identifier.
  escapeQuotes :: Text -> Text
  escapeQuotes = Data.Text.replace "\"" "\"\""
  
  -- | An inductively defined "tuple", or heterogeneous, non-empty list.
  data a :*: b where
    (:*:) :: a -> b -> a :*: b
    deriving (Typeable, Generic)
  infixr 1 :*:
  
  instance (Show a, Show b) => Show (a :*: b) where
    show (a :*: b) = show a ++ " :*: " ++ show b
  
  instance (Eq a, Eq b) => Eq (a :*: b) where
    (a :*: b) == (a' :*: b') = a == a' && b == b'
  
  instance (Ord a, Ord b) => Ord (a :*: b) where
    (a :*: b) `compare` (a' :*: b') =
      case a `compare` a' of
        EQ -> b `compare` b'
        o  -> o
  
  type family Head a where
    Head (a :*: b) = a
    Head a         = a
  
  class Tup a where
    tupHead :: a -> Head a
  
  instance {-# OVERLAPPING #-} Tup (a :*: b) where
    tupHead (a :*: _) = a
  
  instance Head a ~ a => Tup a where
    tupHead a = a
  
  -- | Get the first element of an inductive tuple.
  first :: Tup a => a -> Head a
  first = tupHead
  
  -- | Get the second element of an inductive tuple.
  second :: Tup b => (a :*: b) -> Head b
  second (_ :*: b) = tupHead b
  
  -- | Get the third element of an inductive tuple.
  third :: Tup c => (a :*: b :*: c) -> Head c
  third (_ :*: _ :*: c) = tupHead c
  
  -- | Get the fourth element of an inductive tuple.
  fourth :: Tup d => (a :*: b :*: c :*: d) -> Head d
  fourth (_ :*: _ :*: _ :*: d) = tupHead d
  
  -- | Get the fifth element of an inductive tuple.
  fifth :: Tup e => (a :*: b :*: c :*: d :*: e) -> Head e
  fifth (_ :*: _ :*: _ :*: _ :*: e) = tupHead e
  