{-| Module: Squeal.PostgreSQL.Type Description: types Copyright: (c) Eitan Chatav, 2010 Maintainer: eitan@morphism.tech Stability: experimental storage newtypes -} {-# LANGUAGE AllowAmbiguousTypes , DeriveAnyClass , DeriveFoldable , DeriveFunctor , DeriveGeneric , DeriveTraversable , DerivingStrategies , DefaultSignatures , FlexibleContexts , FlexibleInstances , FunctionalDependencies , GADTs , LambdaCase , MultiParamTypeClasses , OverloadedStrings , ScopedTypeVariables , TypeApplications , TypeFamilies , TypeInType , TypeOperators , UndecidableInstances , UndecidableSuperClasses #-} module Squeal.PostgreSQL.Type ( -- * Storage newtypes Money (..) , Json (..) , Jsonb (..) , Composite (..) , Enumerated (..) , VarArray (..) , FixArray (..) , VarChar, varChar, getVarChar , FixChar, fixChar, getFixChar , Only (..) ) where import Data.Proxy import Data.Int (Int64) import GHC.TypeLits import qualified Data.Text as Strict (Text) import qualified Data.Text as Strict.Text import qualified GHC.Generics as GHC import qualified Generics.SOP as SOP -- $setup -- >>> import Squeal.PostgreSQL {- | The `Money` newtype stores a monetary value in terms of the number of cents, i.e. @$2,000.20@ would be expressed as @Money { cents = 200020 }@. >>> :kind! PG Money PG Money :: PGType = 'PGmoney -} newtype Money = Money { cents :: Int64 } deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `Json` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGjson`. >>> :kind! PG (Json [String]) PG (Json [String]) :: PGType = 'PGjson -} newtype Json hask = Json {getJson :: hask} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `Jsonb` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGjsonb`. >>> :kind! PG (Jsonb [String]) PG (Jsonb [String]) :: PGType = 'PGjsonb -} newtype Jsonb hask = Jsonb {getJsonb :: hask} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `Composite` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGcomposite`. >>> :{ data Complex = Complex { real :: Double , imaginary :: Double } deriving stock GHC.Generic deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo) :} >>> :kind! PG (Composite Complex) PG (Composite Complex) :: PGType = 'PGcomposite '["real" ::: 'NotNull 'PGfloat8, "imaginary" ::: 'NotNull 'PGfloat8] -} newtype Composite record = Composite {getComposite :: record} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `Enumerated` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGenum`. >>> :kind! PG (Enumerated Ordering) PG (Enumerated Ordering) :: PGType = 'PGenum '["LT", "EQ", "GT"] -} newtype Enumerated enum = Enumerated {getEnumerated :: enum} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `VarArray` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGvararray`. >>> import Data.Vector >>> :kind! PG (VarArray (Vector Double)) PG (VarArray (Vector Double)) :: PGType = 'PGvararray ('NotNull 'PGfloat8) -} newtype VarArray arr = VarArray {getVarArray :: arr} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) {- | The `FixArray` newtype is an indication that the Haskell type it's applied to should be stored as a `Squeal.PostgreSQL.Type.Schema.PGfixarray`. >>> :kind! PG (FixArray ((Double, Double), (Double, Double))) PG (FixArray ((Double, Double), (Double, Double))) :: PGType = 'PGfixarray '[2, 2] ('NotNull 'PGfloat8) -} newtype FixArray arr = FixArray {getFixArray :: arr} deriving stock (Eq, Ord, Show, Read, GHC.Generic) deriving anyclass (SOP.HasDatatypeInfo, SOP.Generic) -- | `Only` is a 1-tuple type, useful for encoding or decoding a singleton newtype Only x = Only { fromOnly :: x } deriving (Functor,Foldable,Traversable,Eq,Ord,Read,Show,GHC.Generic) instance SOP.Generic (Only x) instance SOP.HasDatatypeInfo (Only x) {- | Variable-length text type with limit >>> :kind! PG (VarChar 4) PG (VarChar 4) :: PGType = 'PGvarchar 4 -} newtype VarChar (n :: Nat) = VarChar Strict.Text deriving (Eq,Ord,Read,Show) -- | Constructor for `VarChar` varChar :: forall n . KnownNat n => Strict.Text -> Maybe (VarChar n) varChar t = if Strict.Text.length t <= fromIntegral (natVal @n Proxy) then Just $ VarChar t else Nothing -- | Access the `Strict.Text` of a `VarChar` getVarChar :: VarChar n -> Strict.Text getVarChar (VarChar t) = t {- | Fixed-length, blank padded >>> :kind! PG (FixChar 4) PG (FixChar 4) :: PGType = 'PGchar 4 -} newtype FixChar (n :: Nat) = FixChar Strict.Text deriving (Eq,Ord,Read,Show) -- | Constructor for `FixChar` fixChar :: forall n . KnownNat n => Strict.Text -> Maybe (FixChar n) fixChar t = if Strict.Text.length t == fromIntegral (natVal @n Proxy) then Just $ FixChar t else Nothing -- | Access the `Strict.Text` of a `FixChar` getFixChar :: FixChar n -> Strict.Text getFixChar (FixChar t) = t