hasql-1.10: src/library/Hasql/Codecs/Decoders/Array.hs
module Hasql.Codecs.Decoders.Array
( Array,
toValueDecoder,
toTypeSig,
toSchema,
toTypeName,
toBaseOid,
toArrayOid,
toDimensionality,
dimension,
element,
)
where
import Hasql.Codecs.Decoders.NullableOrNot qualified as NullableOrNot
import Hasql.Codecs.Decoders.Value qualified as Value
import Hasql.Codecs.RequestingOid qualified as RequestingOid
import Hasql.Platform.Prelude
import PostgreSQL.Binary.Decoding qualified as Binary
import TextBuilder qualified
-- |
-- Binary generic array decoder.
--
-- Here's how you can use it to produce a specific array value decoder:
--
-- @
-- x :: 'Value.Value' [[Text]]
-- x = 'array' ('dimension' 'replicateM' ('dimension' 'replicateM' ('element' ('nonNullable' 'text'))))
-- @
data Array a
= Array
-- | Schema name.
(Maybe Text)
-- | Type name for the array element.
Text
-- | Statically known OID for the base (element) type.
(Maybe Word32)
-- | Statically known OID for the array type.
(Maybe Word32)
-- | Number of dimensions.
Word
-- | Decoding function
(RequestingOid.RequestingOid Binary.Array a)
deriving (Functor)
{-# INLINE toValueDecoder #-}
toValueDecoder :: Array a -> RequestingOid.RequestingOid Binary.Value a
toValueDecoder (Array _ _ _ _ _ decoder) =
RequestingOid.hoist Binary.array decoder
-- | Get the type signature for the array based on element type name
{-# INLINE toTypeSig #-}
toTypeSig :: Array a -> Text
toTypeSig (Array schemaName elementTypeName _ _ ndims _) =
TextBuilder.toText
( mconcat
( mconcat
[ foldMap
(\s -> [TextBuilder.text s, TextBuilder.char '.'])
schemaName,
[ TextBuilder.text elementTypeName
],
replicate
(fromIntegral ndims)
(TextBuilder.text "[]")
]
)
)
toSchema :: Array a -> Maybe Text
toSchema (Array schema _ _ _ _ _) = schema
-- | Get the type name for the array based on element type name
{-# INLINE toTypeName #-}
toTypeName :: Array a -> Text
toTypeName (Array _ elementTypeName _ _ _ _) =
elementTypeName
-- | Get the base OID if statically known
{-# INLINE toBaseOid #-}
toBaseOid :: Array a -> Maybe Word32
toBaseOid (Array _ _ baseOid _ _ _) = baseOid
-- | Get the array OID if statically known
{-# INLINE toArrayOid #-}
toArrayOid :: Array a -> Maybe Word32
toArrayOid (Array _ _ _ arrayOid _ _) = arrayOid
-- | Get the dimensionality of the array
{-# INLINE toDimensionality #-}
toDimensionality :: Array a -> Word
toDimensionality (Array _ _ _ _ ndims _) = ndims
-- * Public API
-- |
-- Binary function for parsing a dimension of an array.
-- Provides support for multi-dimensional arrays.
--
-- Accepts:
--
-- * An implementation of the @replicateM@ function
-- (@Control.Monad.'Control.Monad.replicateM'@, @Data.Vector.'Data.Vector.replicateM'@),
-- which determines the output value.
--
-- * Binary decoder of its components, which can be either another 'dimension' or 'element'.
{-# INLINEABLE dimension #-}
dimension :: (forall m. (Monad m) => Int -> m a -> m b) -> Array a -> Array b
dimension replicateM (Array schema typeName baseOid arrayOid ndims decoder) =
Array
schema
typeName
baseOid
arrayOid
(succ ndims)
(RequestingOid.hoist (Binary.dimensionArray replicateM) decoder)
-- |
-- Lift a 'Value.Value' decoder into an 'Array' decoder for parsing of leaf values.
{-# INLINEABLE element #-}
element :: NullableOrNot.NullableOrNot Value.Value a -> Array a
element = \case
NullableOrNot.NonNullable imp ->
Array
(Value.toSchema imp)
(Value.toTypeName imp)
(Value.toBaseOid imp)
(Value.toArrayOid imp)
1
(RequestingOid.hoist Binary.valueArray (Value.toDecoder imp))
NullableOrNot.Nullable imp ->
Array
(Value.toSchema imp)
(Value.toTypeName imp)
(Value.toBaseOid imp)
(Value.toArrayOid imp)
1
(RequestingOid.hoist Binary.nullableValueArray (Value.toDecoder imp))