{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiWayIf #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Data.Avro
(
Schema(..)
, Schema.Field(..), Schema.Order(..)
, Schema.TypeName(..)
, Schema.Decimal(..)
, Schema.LogicalTypeBytes(..), Schema.LogicalTypeFixed(..)
, Schema.LogicalTypeInt(..), Schema.LogicalTypeLong(..)
, Schema.LogicalTypeString(..)
, ReadSchema
, deconflict
, readSchemaFromSchema
, encodeValue
, encodeValueWithSchema
, decodeValue
, decodeValueWithSchema
, decodeContainer
, decodeContainerWithEmbeddedSchema
, decodeContainerWithReaderSchema
, encodeContainer
, encodeContainerWithSchema
, encodeContainerWithSync
, Container.newSyncBytes
, extractContainerValuesBytes
, decodeContainerValuesBytes
, ToAvro
, FromAvro
, Codec, nullCodec, deflateCodec
, HasAvroSchema(..)
, schemaOf
) where
import Control.Monad ((>=>))
import Data.Avro.Codec (Codec, deflateCodec, nullCodec)
import Data.Avro.Encoding.FromAvro
import Data.Avro.Encoding.ToAvro
import Data.Avro.HasAvroSchema
import qualified Data.Avro.Internal.Container as Container
import Data.Avro.Schema.Deconflict (deconflict)
import Data.Avro.Schema.ReadSchema (ReadSchema, fromSchema)
import Data.Avro.Schema.Schema (Schema)
import qualified Data.Avro.Schema.Schema as Schema
import Data.Binary.Get (runGetOrFail)
import Data.ByteString.Builder (toLazyByteString)
import qualified Data.ByteString.Lazy as BL
import Data.Tagged (untag)
readSchemaFromSchema :: Schema -> ReadSchema
readSchemaFromSchema = fromSchema
{-# INLINE readSchemaFromSchema #-}
encodeValueWithSchema :: ToAvro a => Schema -> a -> BL.ByteString
encodeValueWithSchema s = toLazyByteString . toAvro s
{-# INLINE encodeValueWithSchema #-}
encodeValue :: (HasAvroSchema a, ToAvro a) => a -> BL.ByteString
encodeValue a = encodeValueWithSchema (schemaOf a) a
{-# INLINE encodeValue #-}
decodeValueWithSchema :: FromAvro a => ReadSchema -> BL.ByteString -> Either String a
decodeValueWithSchema schema payload =
case runGetOrFail (getValue schema) payload of
Right (bs, _, v) -> fromAvro v
Left (_, _, e) -> Left e
decodeValue :: forall a. (HasAvroSchema a, FromAvro a) => BL.ByteString -> Either String a
decodeValue = decodeValueWithSchema (fromSchema (untag @a schema))
{-# INLINE decodeValue #-}
decodeContainer :: forall a. (HasAvroSchema a, FromAvro a) => BL.ByteString -> [Either String a]
decodeContainer = decodeContainerWithReaderSchema (untag @a schema)
{-# INLINE decodeContainer #-}
decodeContainerWithEmbeddedSchema :: forall a. FromAvro a => BL.ByteString -> [Either String a]
decodeContainerWithEmbeddedSchema payload =
case Container.extractContainerValues (pure . fromSchema) (getValue >=> (either fail pure . fromAvro)) payload of
Left err -> [Left err]
Right (_, values) -> values
decodeContainerWithReaderSchema :: forall a. FromAvro a => Schema -> BL.ByteString -> [Either String a]
decodeContainerWithReaderSchema readerSchema payload =
case Container.extractContainerValues (flip deconflict readerSchema) (getValue >=> (either fail pure . fromAvro)) payload of
Left err -> [Left err]
Right (_, values) -> values
extractContainerValuesBytes :: BL.ByteString -> Either String (Schema, [Either String BL.ByteString])
extractContainerValuesBytes =
(fmap . fmap . fmap . fmap) snd . Container.extractContainerValuesBytes (pure . fromSchema) getValue
{-# INLINE extractContainerValuesBytes #-}
decodeContainerValuesBytes :: forall a. FromAvro a
=> Schema
-> BL.ByteString
-> Either String (Schema, [Either String (a, BL.ByteString)])
decodeContainerValuesBytes readerSchema =
Container.extractContainerValuesBytes (flip deconflict readerSchema) (getValue >=> (either fail pure . fromAvro))
{-# INLINE decodeContainerValuesBytes #-}
encodeContainer :: forall a. (HasAvroSchema a, ToAvro a) => Codec -> [[a]] -> IO BL.ByteString
encodeContainer codec = encodeContainerWithSchema codec (untag @a schema)
encodeContainerWithSchema :: ToAvro a => Codec -> Schema -> [[a]] -> IO BL.ByteString
encodeContainerWithSchema codec sch xss =
do sync <- Container.newSyncBytes
return $ encodeContainerWithSync codec sch sync xss
encodeContainerWithSync :: ToAvro a => Codec -> Schema -> BL.ByteString -> [[a]] -> BL.ByteString
encodeContainerWithSync = Container.packContainerValuesWithSync' toAvro
{-# INLINE encodeContainerWithSync #-}