hydra-0.15.0: src/main/haskell/Hydra/Sources/Avro/SchemaJson.hs
module Hydra.Sources.Avro.SchemaJson where
-- Standard imports for term-level sources outside of the kernel
import Hydra.Kernel
import Hydra.Sources.Libraries
import Hydra.Dsl.Meta.Lib.Strings as Strings
import Hydra.Dsl.Meta.Phantoms as Phantoms
import qualified Hydra.Dsl.Annotations as Annotations
import qualified Hydra.Dsl.Bootstrap as Bootstrap
import qualified Hydra.Dsl.LiteralTypes as LiteralTypes
import qualified Hydra.Dsl.Literals as Literals
import qualified Hydra.Dsl.Paths as Paths
import qualified Hydra.Dsl.Ast as Ast
import qualified Hydra.Dsl.Meta.Base as MetaBase
import qualified Hydra.Dsl.Coders as Coders
import qualified Hydra.Dsl.Util as Util
import qualified Hydra.Dsl.Meta.Core as Core
import qualified Hydra.Dsl.Meta.Graph as Graph
import qualified Hydra.Dsl.Json.Model as Json
import qualified Hydra.Dsl.Meta.Lib.Chars as Chars
import qualified Hydra.Dsl.Meta.Lib.Eithers as Eithers
import qualified Hydra.Dsl.Meta.Lib.Equality as Equality
import qualified Hydra.Dsl.Meta.Lib.Lists as Lists
import qualified Hydra.Dsl.Meta.Lib.Literals as Literals
import qualified Hydra.Dsl.Meta.Lib.Logic as Logic
import qualified Hydra.Dsl.Meta.Lib.Maps as Maps
import qualified Hydra.Dsl.Meta.Lib.Math as Math
import qualified Hydra.Dsl.Meta.Lib.Maybes as Maybes
import qualified Hydra.Dsl.Meta.Lib.Pairs as Pairs
import qualified Hydra.Dsl.Meta.Lib.Sets as Sets
import qualified Hydra.Dsl.Packaging as Packaging
import qualified Hydra.Dsl.Meta.Terms as MetaTerms
import qualified Hydra.Dsl.Meta.Testing as Testing
import qualified Hydra.Dsl.Topology as Topology
import qualified Hydra.Dsl.Meta.Types as MetaTypes
import qualified Hydra.Dsl.Typing as Typing
import qualified Hydra.Dsl.Util as Util
import qualified Hydra.Dsl.Meta.Variants as Variants
import qualified Hydra.Dsl.Prims as Prims
import qualified Hydra.Dsl.Meta.Tabular as Tabular
import qualified Hydra.Dsl.Terms as Terms
import qualified Hydra.Dsl.Tests as Tests
import qualified Hydra.Dsl.Types as Types
import qualified Hydra.Sources.Decode.Core as DecodeCore
import qualified Hydra.Sources.Encode.Core as EncodeCore
import qualified Hydra.Sources.Kernel.Terms.Adapt as Adapt
import qualified Hydra.Sources.Kernel.Terms.All as KernelTerms
import qualified Hydra.Sources.Kernel.Terms.Annotations as Annotations
import qualified Hydra.Sources.Kernel.Terms.Arity as Arity
import qualified Hydra.Sources.Kernel.Terms.Checking as Checking
import qualified Hydra.Sources.Kernel.Terms.Constants as Constants
import qualified Hydra.Sources.Kernel.Terms.Extract.Core as ExtractCore
import qualified Hydra.Sources.Kernel.Terms.Extract.Util as ExtractUtil
import qualified Hydra.Sources.Kernel.Terms.Formatting as Formatting
import qualified Hydra.Sources.Kernel.Terms.Inference as Inference
import qualified Hydra.Sources.Kernel.Terms.Languages as Languages
import qualified Hydra.Sources.Kernel.Terms.Lexical as Lexical
import qualified Hydra.Sources.Kernel.Terms.Literals as Literals
import qualified Hydra.Sources.Kernel.Terms.Names as Names
import qualified Hydra.Sources.Kernel.Terms.Reduction as Reduction
import qualified Hydra.Sources.Kernel.Terms.Reflect as Reflect
import qualified Hydra.Sources.Kernel.Terms.Serialization as Serialization
import qualified Hydra.Sources.Kernel.Terms.Show.Paths as ShowPaths
import qualified Hydra.Sources.Kernel.Terms.Show.Core as ShowCore
import qualified Hydra.Sources.Kernel.Terms.Show.Graph as ShowGraph
import qualified Hydra.Sources.Kernel.Terms.Show.Variants as ShowVariants
import qualified Hydra.Sources.Kernel.Terms.Show.Typing as ShowTyping
import qualified Hydra.Sources.Kernel.Terms.Sorting as Sorting
import qualified Hydra.Sources.Kernel.Terms.Substitution as Substitution
import qualified Hydra.Sources.Kernel.Terms.Templates as Templates
import qualified Hydra.Sources.Kernel.Terms.Unification as Unification
import qualified Hydra.Sources.Kernel.Types.All as KernelTypes
import Prelude hiding ((++))
import qualified Data.Int as I
import qualified Data.List as L
import qualified Data.Map as M
import qualified Data.Scientific as Sci
import qualified Data.Set as S
import qualified Data.Maybe as Y
-- Additional imports
import qualified Hydra.Dsl.Meta.Context as Ctx
import qualified Hydra.Dsl.Errors as Error
import qualified Hydra.Avro.Schema as Avro
import qualified Hydra.Json.Model as JM
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Sources.Avro.Schema as AvroSchema
-- Result type alias (was previously imported from Staging module)
type Result a = Either Error a
define :: String -> TTerm a -> TTermDefinition a
define = definitionInModule module_
avroSchemaPhantomNs :: Namespace
avroSchemaPhantomNs = Namespace "hydra.avro.schema"
jsonModelNs :: Namespace
jsonModelNs = Namespace "hydra.json.model"
jsonParserNs :: Namespace
jsonParserNs = Namespace "hydra.json.parser"
jsonWriterNs :: Namespace
jsonWriterNs = Namespace "hydra.json.writer"
ns :: Namespace
ns = Namespace "hydra.avro.schemaJson"
module_ :: Module
module_ = Module {
moduleNamespace = ns,
moduleDefinitions = definitions,
moduleTermDependencies = [jsonWriterNs, jsonParserNs],
moduleTypeDependencies = (AvroSchema.ns:jsonModelNs:Namespace "hydra.parsing":KernelTypes.kernelTypesNamespaces),
moduleDescription = Just "JSON serialization and deserialization for Avro schemas"}
where
definitions = [
-- String constants
toDefinition avro_aliases,
toDefinition avro_array,
toDefinition avro_ascending,
toDefinition avro_boolean,
toDefinition avro_bytes,
toDefinition avro_default,
toDefinition avro_descending,
toDefinition avro_doc,
toDefinition avro_double,
toDefinition avro_enum,
toDefinition avro_fields,
toDefinition avro_fixed,
toDefinition avro_float,
toDefinition avro_ignore,
toDefinition avro_int,
toDefinition avro_items,
toDefinition avro_long,
toDefinition avro_map,
toDefinition avro_name,
toDefinition avro_namespace,
toDefinition avro_null,
toDefinition avro_order,
toDefinition avro_record,
toDefinition avro_size,
toDefinition avro_string,
toDefinition avro_symbols,
toDefinition avro_type,
toDefinition avro_values,
-- Error helpers
toDefinition err,
toDefinition unexpectedE,
-- JSON extraction helpers
toDefinition expectArrayE,
toDefinition expectNumberE,
toDefinition expectObjectE,
toDefinition expectStringE,
toDefinition requireE,
toDefinition requireArrayE,
toDefinition requireNumberE,
toDefinition requireStringE,
toDefinition optE,
toDefinition optArrayE,
toDefinition optStringE,
toDefinition showJsonValue,
toDefinition stringToJsonValue,
-- Encode functions
toDefinition encodeSchema,
toDefinition encodePrimitive,
toDefinition encodeArray,
toDefinition encodeMap,
toDefinition encodeNamed,
toDefinition encodeNamedType,
toDefinition encodeEnumE,
toDefinition encodeFixedE,
toDefinition encodeRecordE,
toDefinition encodeFieldE,
toDefinition encodeOrderE,
toDefinition encodeUnion,
toDefinition encodeAnnotations,
-- Coder functions
toDefinition avroSchemaJsonCoder,
toDefinition avroSchemaStringCoder,
-- Decode functions
toDefinition decodeAliases,
toDefinition decodeArraySchema,
toDefinition decodeEnum,
toDefinition decodeField,
toDefinition decodeFixed,
toDefinition decodeMapSchema,
toDefinition decodeNamedSchema,
toDefinition decodeObjectSchema,
toDefinition decodeOrder,
toDefinition decodePrimitiveName,
toDefinition decodeRecord,
toDefinition decodeSchema,
toDefinition getAnnotations]
-- | Error helpers
err :: TTermDefinition (Context -> String -> Result a)
err = define "err" $
doc "Construct an error result with a message in context" $
lambda "cx" $ lambda "msg" $
Ctx.failInContext (Error.errorOther $ Error.otherError (var "msg")) (var "cx")
unexpectedE :: TTermDefinition (Context -> String -> String -> Result a)
unexpectedE = define "unexpectedE" $
doc "Construct an error for unexpected values" $
lambda "cx" $ lambda "expected" $ lambda "found" $
err @@ var "cx" @@ (Strings.cat $ list [string "Expected ", var "expected", string ", found: ", var "found"])
-- | JSON extraction helpers
expectArrayE :: TTermDefinition (Context -> JM.Value -> Result [JM.Value])
expectArrayE = define "expectArrayE" $
doc "Extract a JSON array or return an error" $
lambda "cx" $ lambda "value" $
cases JM._Value (var "value") Nothing [
JM._Value_array>>: lambda "v" $ Phantoms.right (var "v")]
expectNumberE :: TTermDefinition (Context -> JM.Value -> Result Sci.Scientific)
expectNumberE = define "expectNumberE" $
doc "Extract a JSON number or return an error" $
lambda "cx" $ lambda "value" $
cases JM._Value (var "value") Nothing [
JM._Value_number>>: lambda "v" $ Phantoms.right (var "v")]
expectObjectE :: TTermDefinition (Context -> JM.Value -> Result (M.Map String JM.Value))
expectObjectE = define "expectObjectE" $
doc "Extract a JSON object or return an error" $
lambda "cx" $ lambda "value" $
cases JM._Value (var "value") Nothing [
JM._Value_object>>: lambda "v" $ Phantoms.right (var "v")]
expectStringE :: TTermDefinition (Context -> JM.Value -> Result String)
expectStringE = define "expectStringE" $
doc "Extract a JSON string or return an error" $
lambda "cx" $ lambda "value" $
cases JM._Value (var "value") Nothing [
JM._Value_string>>: lambda "v" $ Phantoms.right (var "v")]
requireE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result JM.Value)
requireE = define "requireE" $
doc "Look up a required attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Maybes.maybe
(err @@ var "cx" @@ (Strings.cat $ list [string "required attribute ", Literals.showString (var "fname"), string " not found"]))
(lambda "v" $ Phantoms.right (var "v"))
(Maps.lookup (var "fname") (var "m"))
requireArrayE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result [JM.Value])
requireArrayE = define "requireArrayE" $
doc "Look up a required array attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Eithers.bind (requireE @@ var "cx" @@ var "fname" @@ var "m")
(lambda "v" $ expectArrayE @@ var "cx" @@ var "v")
requireNumberE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result Sci.Scientific)
requireNumberE = define "requireNumberE" $
doc "Look up a required number attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Eithers.bind (requireE @@ var "cx" @@ var "fname" @@ var "m")
(lambda "v" $ expectNumberE @@ var "cx" @@ var "v")
requireStringE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result String)
requireStringE = define "requireStringE" $
doc "Look up a required string attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Eithers.bind (requireE @@ var "cx" @@ var "fname" @@ var "m")
(lambda "v" $ expectStringE @@ var "cx" @@ var "v")
optE :: TTermDefinition (String -> M.Map String JM.Value -> Maybe JM.Value)
optE = define "optE" $
doc "Look up an optional attribute in a JSON object map" $
lambda "k" $ lambda "m" $
Maps.lookup (var "k") (var "m")
optArrayE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result (Maybe [JM.Value]))
optArrayE = define "optArrayE" $
doc "Look up an optional array attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Maybes.maybe
(Phantoms.right nothing)
(lambda "v" $ Eithers.map (lambda "a" $ Maybes.pure (var "a")) (expectArrayE @@ var "cx" @@ var "v"))
(Maps.lookup (var "fname") (var "m"))
optStringE :: TTermDefinition (Context -> String -> M.Map String JM.Value -> Result (Maybe String))
optStringE = define "optStringE" $
doc "Look up an optional string attribute in a JSON object map" $
lambda "cx" $ lambda "fname" $ lambda "m" $
Maybes.maybe
(Phantoms.right nothing)
(lambda "v" $ Eithers.map (lambda "s" $ Maybes.pure (var "s")) (expectStringE @@ var "cx" @@ var "v"))
(Maps.lookup (var "fname") (var "m"))
showJsonValue :: TTermDefinition (JM.Value -> String)
showJsonValue = define "showJsonValue" $
doc "Convert a JSON value to its string representation" $
lambda "v" $
var "hydra.json.writer.printJson" @@ var "v"
stringToJsonValue :: TTermDefinition (String -> Either String JM.Value)
stringToJsonValue = define "stringToJsonValue" $
doc "Parse a JSON string, returning Either for compatibility" $
lambda "s" $
cases Parsing._ParseResult (var "hydra.json.parser.parseJson" @@ var "s") Nothing [
Parsing._ParseResult_success>>: lambda "success" $
Phantoms.right (project Parsing._ParseSuccess Parsing._ParseSuccess_value @@ var "success"),
Parsing._ParseResult_failure>>: lambda "failure" $
Phantoms.left (project Parsing._ParseError Parsing._ParseError_message @@ var "failure")]
-- | String constants
avro_aliases :: TTermDefinition String
avro_aliases = define "avro_aliases" $ string "aliases"
avro_array :: TTermDefinition String
avro_array = define "avro_array" $ string "array"
avro_ascending :: TTermDefinition String
avro_ascending = define "avro_ascending" $ string "ascending"
avro_boolean :: TTermDefinition String
avro_boolean = define "avro_boolean" $ string "boolean"
avro_bytes :: TTermDefinition String
avro_bytes = define "avro_bytes" $ string "bytes"
avro_default :: TTermDefinition String
avro_default = define "avro_default" $ string "default"
avro_descending :: TTermDefinition String
avro_descending = define "avro_descending" $ string "descending"
avro_doc :: TTermDefinition String
avro_doc = define "avro_doc" $ string "doc"
avro_double :: TTermDefinition String
avro_double = define "avro_double" $ string "double"
avro_enum :: TTermDefinition String
avro_enum = define "avro_enum" $ string "enum"
avro_fields :: TTermDefinition String
avro_fields = define "avro_fields" $ string "fields"
avro_fixed :: TTermDefinition String
avro_fixed = define "avro_fixed" $ string "fixed"
avro_float :: TTermDefinition String
avro_float = define "avro_float" $ string "float"
avro_ignore :: TTermDefinition String
avro_ignore = define "avro_ignore" $ string "ignore"
avro_int :: TTermDefinition String
avro_int = define "avro_int" $ string "int"
avro_items :: TTermDefinition String
avro_items = define "avro_items" $ string "items"
avro_long :: TTermDefinition String
avro_long = define "avro_long" $ string "long"
avro_map :: TTermDefinition String
avro_map = define "avro_map" $ string "map"
avro_name :: TTermDefinition String
avro_name = define "avro_name" $ string "name"
avro_namespace :: TTermDefinition String
avro_namespace = define "avro_namespace" $ string "namespace"
avro_null :: TTermDefinition String
avro_null = define "avro_null" $ string "null"
avro_order :: TTermDefinition String
avro_order = define "avro_order" $ string "order"
avro_record :: TTermDefinition String
avro_record = define "avro_record" $ string "record"
avro_size :: TTermDefinition String
avro_size = define "avro_size" $ string "size"
avro_string :: TTermDefinition String
avro_string = define "avro_string" $ string "string"
avro_symbols :: TTermDefinition String
avro_symbols = define "avro_symbols" $ string "symbols"
avro_type :: TTermDefinition String
avro_type = define "avro_type" $ string "type"
avro_values :: TTermDefinition String
avro_values = define "avro_values" $ string "values"
-- | Encode functions
encodeSchema :: TTermDefinition (Avro.Schema -> JM.Value)
encodeSchema = define "encodeSchema" $
doc "Encode an Avro schema to a JSON value" $
lambda "schema" $
cases Avro._Schema (var "schema") Nothing [
Avro._Schema_primitive>>: lambda "p" $ encodePrimitive @@ var "p",
Avro._Schema_array>>: lambda "arr" $ encodeArray @@ var "arr",
Avro._Schema_map>>: lambda "mp" $ encodeMap @@ var "mp",
Avro._Schema_named>>: lambda "n" $ encodeNamed @@ var "n",
Avro._Schema_reference>>: lambda "ref" $ inject JM._Value JM._Value_string (var "ref"),
Avro._Schema_union>>: lambda "u" $ encodeUnion @@ var "u"]
encodePrimitive :: TTermDefinition (Avro.Primitive -> JM.Value)
encodePrimitive = define "encodePrimitive" $
doc "Encode an Avro primitive type as a JSON string" $
lambda "p" $
inject JM._Value JM._Value_string
(cases Avro._Primitive (var "p") Nothing [
Avro._Primitive_null>>: constant (string "null"),
Avro._Primitive_boolean>>: constant (string "boolean"),
Avro._Primitive_int>>: constant (string "int"),
Avro._Primitive_long>>: constant (string "long"),
Avro._Primitive_float>>: constant (string "float"),
Avro._Primitive_double>>: constant (string "double"),
Avro._Primitive_bytes>>: constant (string "bytes"),
Avro._Primitive_string>>: constant (string "string")])
encodeArray :: TTermDefinition (Avro.Array -> JM.Value)
encodeArray = define "encodeArray" $
doc "Encode an Avro array schema to a JSON object" $
lambda "arr" $
inject JM._Value JM._Value_object
(Maps.fromList (list [
pair (string "type") (inject JM._Value JM._Value_string (string "array")),
pair (string "items") (encodeSchema @@ (project Avro._Array Avro._Array_items @@ var "arr"))]))
encodeMap :: TTermDefinition (Avro.Map -> JM.Value)
encodeMap = define "encodeMap" $
doc "Encode an Avro map schema to a JSON object" $
lambda "mp" $
inject JM._Value JM._Value_object
(Maps.fromList (list [
pair (string "type") (inject JM._Value JM._Value_string (string "map")),
pair (string "values") (encodeSchema @@ (project Avro._Map Avro._Map_values @@ var "mp"))]))
encodeNamed :: TTermDefinition (Avro.Named -> JM.Value)
encodeNamed = define "encodeNamed" $
doc "Encode an Avro named type to a JSON object" $
lambda "n" $
inject JM._Value JM._Value_object
(Maps.fromList (Lists.concat (list [
list [pair (string "name") (inject JM._Value JM._Value_string (project Avro._Named Avro._Named_name @@ var "n"))],
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "ns" $ list [pair (string "namespace") (inject JM._Value JM._Value_string (var "ns"))]) (project Avro._Named Avro._Named_namespace @@ var "n"),
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "d" $ list [pair (string "doc") (inject JM._Value JM._Value_string (var "d"))]) (project Avro._Named Avro._Named_doc @@ var "n"),
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "als" $ list [pair (string "aliases") (inject JM._Value JM._Value_array (Lists.map (lambda "a" $ inject JM._Value JM._Value_string (var "a")) (var "als")))]) (project Avro._Named Avro._Named_aliases @@ var "n"),
encodeNamedType @@ (project Avro._Named Avro._Named_type @@ var "n"),
encodeAnnotations @@ (project Avro._Named Avro._Named_annotations @@ var "n")])))
encodeNamedType :: TTermDefinition (Avro.NamedType -> [(String, JM.Value)])
encodeNamedType = define "encodeNamedType" $
doc "Encode the specific variant of a named Avro type" $
lambda "nt" $
cases Avro._NamedType (var "nt") Nothing [
Avro._NamedType_enum>>: lambda "e" $ encodeEnumE @@ var "e",
Avro._NamedType_fixed>>: lambda "f" $ encodeFixedE @@ var "f",
Avro._NamedType_record>>: lambda "r" $ encodeRecordE @@ var "r"]
encodeEnumE :: TTermDefinition (Avro.Enum -> [(String, JM.Value)])
encodeEnumE = define "encodeEnum" $
doc "Encode an Avro enum type as key-value pairs" $
lambda "e" $
Lists.concat (list [
list [pair (string "type") (inject JM._Value JM._Value_string (string "enum"))],
list [pair (string "symbols") (inject JM._Value JM._Value_array (Lists.map (lambda "s" $ inject JM._Value JM._Value_string (var "s")) (project Avro._Enum Avro._Enum_symbols @@ var "e")))],
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "d" $ list [pair (string "default") (inject JM._Value JM._Value_string (var "d"))]) (project Avro._Enum Avro._Enum_default @@ var "e")])
encodeFixedE :: TTermDefinition (Avro.Fixed -> [(String, JM.Value)])
encodeFixedE = define "encodeFixed" $
doc "Encode an Avro fixed type as key-value pairs" $
lambda "f" $
list [
pair (string "type") (inject JM._Value JM._Value_string (string "fixed")),
pair (string "size") (inject JM._Value JM._Value_number (Literals.bigintToDecimal (Literals.int32ToBigint (project Avro._Fixed Avro._Fixed_size @@ var "f"))))]
encodeRecordE :: TTermDefinition (Avro.Record -> [(String, JM.Value)])
encodeRecordE = define "encodeRecord" $
doc "Encode an Avro record type as key-value pairs" $
lambda "r" $
list [
pair (string "type") (inject JM._Value JM._Value_string (string "record")),
pair (string "fields") (inject JM._Value JM._Value_array (Lists.map encodeFieldE (project Avro._Record Avro._Record_fields @@ var "r")))]
encodeFieldE :: TTermDefinition (Avro.Field -> JM.Value)
encodeFieldE = define "encodeField" $
doc "Encode an Avro field to a JSON object" $
lambda "f" $
inject JM._Value JM._Value_object
(Maps.fromList (Lists.concat (list [
list [pair (string "name") (inject JM._Value JM._Value_string (project Avro._Field Avro._Field_name @@ var "f"))],
list [pair (string "type") (encodeSchema @@ (project Avro._Field Avro._Field_type @@ var "f"))],
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "d" $ list [pair (string "doc") (inject JM._Value JM._Value_string (var "d"))]) (project Avro._Field Avro._Field_doc @@ var "f"),
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "d" $ list [pair (string "default") (var "d")]) (project Avro._Field Avro._Field_default @@ var "f"),
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "o" $ list [encodeOrderE @@ var "o"]) (project Avro._Field Avro._Field_order @@ var "f"),
Maybes.maybe (list ([] :: [TTerm (String, JM.Value)])) (lambda "als" $ list [pair (string "aliases") (inject JM._Value JM._Value_array (Lists.map (lambda "a" $ inject JM._Value JM._Value_string (var "a")) (var "als")))]) (project Avro._Field Avro._Field_aliases @@ var "f"),
encodeAnnotations @@ (project Avro._Field Avro._Field_annotations @@ var "f")])))
encodeOrderE :: TTermDefinition (Avro.Order -> (String, JM.Value))
encodeOrderE = define "encodeOrder" $
doc "Encode an Avro field ordering as a key-value pair" $
lambda "o" $
pair (string "order") (inject JM._Value JM._Value_string
(cases Avro._Order (var "o") Nothing [
Avro._Order_ascending>>: constant (string "ascending"),
Avro._Order_descending>>: constant (string "descending"),
Avro._Order_ignore>>: constant (string "ignore")]))
encodeUnion :: TTermDefinition (Avro.Union -> JM.Value)
encodeUnion = define "encodeUnion" $
doc "Encode an Avro union as a JSON array of schemas" $
lambda "u" $
inject JM._Value JM._Value_array
(Lists.map encodeSchema (unwrap Avro._Union @@ var "u"))
encodeAnnotations :: TTermDefinition (M.Map String JM.Value -> [(String, JM.Value)])
encodeAnnotations = define "encodeAnnotations" $
doc "Encode annotations as key-value pairs with @ prefix on keys" $
lambda "m" $
Lists.map
(lambda "entry" $ pair
(Strings.cat2 (string "@") (Pairs.first (var "entry")))
(Pairs.second (var "entry")))
(Maps.toList (var "m"))
-- | Coder functions
avroSchemaJsonCoder :: TTermDefinition (Context -> Coder Avro.Schema JM.Value)
avroSchemaJsonCoder = define "avroSchemaJsonCoder" $
doc "Create a coder between Avro schemas and JSON values" $
lambda "cx" $
record _Coder [
_Coder_encode>>: lambda "_cx" $ lambda "schema" $ Phantoms.right (encodeSchema @@ var "schema"),
_Coder_decode>>: lambda "cx2" $ lambda "json" $ decodeSchema @@ var "cx2" @@ var "json"]
avroSchemaStringCoder :: TTermDefinition (Context -> Coder Avro.Schema String)
avroSchemaStringCoder = define "avroSchemaStringCoder" $
doc "Create a coder between Avro schemas and JSON strings" $
lambda "cx" $
record _Coder [
_Coder_encode>>: lambda "_cx" $ lambda "schema" $
Phantoms.right (showJsonValue @@ (encodeSchema @@ var "schema")),
_Coder_decode>>: lambda "cx2" $ lambda "s" $
Eithers.bind
(Eithers.either_
(lambda "e" $ err @@ var "cx2" @@ var "e")
(lambda "v" $ Phantoms.right (var "v"))
(stringToJsonValue @@ var "s"))
(lambda "json" $ decodeSchema @@ var "cx2" @@ var "json")]
-- | Decode functions
decodeAliases :: TTermDefinition (Context -> M.Map String JM.Value -> Result (Maybe [String]))
decodeAliases = define "decodeAliases" $
doc "Decode aliases from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (optArrayE @@ var "cx" @@ avro_aliases @@ var "m")
(lambda "mArr" $
Maybes.maybe
(Phantoms.right nothing)
(lambda "arr" $
Eithers.map
(lambda "strs" $ Maybes.pure (var "strs"))
(Eithers.mapList (expectStringE @@ var "cx") (var "arr")))
(var "mArr"))
decodeEnum :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.NamedType)
decodeEnum = define "decodeEnum" $
doc "Decode an Avro enum type from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireArrayE @@ var "cx" @@ avro_symbols @@ var "m")
(lambda "syms" $
Eithers.bind (Eithers.mapList (expectStringE @@ var "cx") (var "syms"))
(lambda "symbols" $
Eithers.bind (optStringE @@ var "cx" @@ avro_default @@ var "m")
(lambda "defVal" $
Phantoms.right (inject Avro._NamedType Avro._NamedType_enum
(record Avro._Enum [
Avro._Enum_symbols>>: var "symbols",
Avro._Enum_default>>: var "defVal"])))))
decodeField :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.Field)
decodeField = define "decodeField" $
doc "Decode an Avro field from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireStringE @@ var "cx" @@ avro_name @@ var "m")
(lambda "name" $
Eithers.bind (optStringE @@ var "cx" @@ avro_doc @@ var "m")
(lambda "fdoc" $
Eithers.bind (requireE @@ var "cx" @@ avro_type @@ var "m")
(lambda "typeJson" $
Eithers.bind (decodeSchema @@ var "cx" @@ var "typeJson")
(lambda "fieldType" $
Eithers.bind (Eithers.bind (optStringE @@ var "cx" @@ avro_order @@ var "m")
(lambda "mOrd" $
Eithers.mapMaybe (decodeOrder @@ var "cx") (var "mOrd")))
(lambda "order" $
Eithers.bind (decodeAliases @@ var "cx" @@ var "m")
(lambda "aliases" $
Phantoms.right (record Avro._Field [
Avro._Field_name>>: var "name",
Avro._Field_doc>>: var "fdoc",
Avro._Field_type>>: var "fieldType",
Avro._Field_default>>: optE @@ avro_default @@ var "m",
Avro._Field_order>>: var "order",
Avro._Field_aliases>>: var "aliases",
Avro._Field_annotations>>: getAnnotations @@ var "m"])))))))
decodeFixed :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.NamedType)
decodeFixed = define "decodeFixed" $
doc "Decode an Avro fixed type from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireNumberE @@ var "cx" @@ avro_size @@ var "m")
(lambda "n" $
lets ["size">: Literals.bigintToInt32 (Literals.decimalToBigint (var "n"))] $
Phantoms.right $ inject Avro._NamedType Avro._NamedType_fixed $
record Avro._Fixed [
Avro._Fixed_size>>: var "size"])
decodeNamedSchema :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.NamedType -> Result Avro.Schema)
decodeNamedSchema = define "decodeNamedSchema" $
doc "Decode a named Avro schema from a JSON object map and a decoded named type result" $
lambda "cx" $ lambda "m" $ lambda "namedTypeResult" $
Eithers.bind (requireStringE @@ var "cx" @@ avro_name @@ var "m")
(lambda "name" $
Eithers.bind (optStringE @@ var "cx" @@ avro_namespace @@ var "m")
(lambda "ns" $
Eithers.bind (optStringE @@ var "cx" @@ avro_doc @@ var "m")
(lambda "sdoc" $
Eithers.bind (decodeAliases @@ var "cx" @@ var "m")
(lambda "aliases" $
Eithers.bind (var "namedTypeResult")
(lambda "namedType" $
Phantoms.right (inject Avro._Schema Avro._Schema_named
(record Avro._Named [
Avro._Named_name>>: var "name",
Avro._Named_namespace>>: var "ns",
Avro._Named_aliases>>: var "aliases",
Avro._Named_doc>>: var "sdoc",
Avro._Named_type>>: var "namedType",
Avro._Named_annotations>>: getAnnotations @@ var "m"])))))))
decodeOrder :: TTermDefinition (Context -> String -> Result Avro.Order)
decodeOrder = define "decodeOrder" $
doc "Decode an Avro field ordering from a string" $
lambda "cx" $ lambda "o" $
Logic.ifElse (Equality.equal (var "o") (string "ascending"))
(Phantoms.right (inject Avro._Order Avro._Order_ascending unit))
(Logic.ifElse (Equality.equal (var "o") (string "descending"))
(Phantoms.right (inject Avro._Order Avro._Order_descending unit))
(Logic.ifElse (Equality.equal (var "o") (string "ignore"))
(Phantoms.right (inject Avro._Order Avro._Order_ignore unit))
(err @@ var "cx" @@ (Strings.cat $ list [string "unknown order: ", var "o"]))))
decodeRecord :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.NamedType)
decodeRecord = define "decodeRecord" $
doc "Decode an Avro record type from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireArrayE @@ var "cx" @@ avro_fields @@ var "m")
(lambda "fieldJsons" $
Eithers.bind (Eithers.mapList
(lambda "fj" $
Eithers.bind (expectObjectE @@ var "cx" @@ var "fj")
(lambda "fm" $ decodeField @@ var "cx" @@ var "fm"))
(var "fieldJsons"))
(lambda "fields" $
Phantoms.right (inject Avro._NamedType Avro._NamedType_record
(record Avro._Record [
Avro._Record_fields>>: var "fields"]))))
decodePrimitiveName :: TTermDefinition (String -> Maybe Avro.Primitive)
decodePrimitiveName = define "decodePrimitiveName" $
doc "Decode a primitive type name string to a Primitive, or Nothing if not a primitive" $
lambda "s" $
Logic.ifElse (Equality.equal (var "s") (string "null"))
(just (inject Avro._Primitive Avro._Primitive_null unit))
(Logic.ifElse (Equality.equal (var "s") (string "boolean"))
(just (inject Avro._Primitive Avro._Primitive_boolean unit))
(Logic.ifElse (Equality.equal (var "s") (string "int"))
(just (inject Avro._Primitive Avro._Primitive_int unit))
(Logic.ifElse (Equality.equal (var "s") (string "long"))
(just (inject Avro._Primitive Avro._Primitive_long unit))
(Logic.ifElse (Equality.equal (var "s") (string "float"))
(just (inject Avro._Primitive Avro._Primitive_float unit))
(Logic.ifElse (Equality.equal (var "s") (string "double"))
(just (inject Avro._Primitive Avro._Primitive_double unit))
(Logic.ifElse (Equality.equal (var "s") (string "bytes"))
(just (inject Avro._Primitive Avro._Primitive_bytes unit))
(Logic.ifElse (Equality.equal (var "s") (string "string"))
(just (inject Avro._Primitive Avro._Primitive_string unit))
nothing)))))))
decodeSchema :: TTermDefinition (Context -> JM.Value -> Result Avro.Schema)
decodeSchema = define "decodeSchema" $
doc "Decode an Avro schema from a JSON value" $
lambda "cx" $ lambda "v" $
cases JM._Value (var "v") (Just (err @@ var "cx" @@ (Strings.cat $ list [string "unexpected JSON value for schema: ", showJsonValue @@ var "v"]))) [
-- String: primitive type name or reference
JM._Value_string>>: lambda "s" $
Maybes.maybe
(Phantoms.right (inject Avro._Schema Avro._Schema_reference (var "s")))
(lambda "p" $ Phantoms.right (inject Avro._Schema Avro._Schema_primitive (var "p")))
(decodePrimitiveName @@ var "s"),
-- Array: union type
JM._Value_array>>: lambda "schemas" $
Eithers.map
(lambda "decoded" $ inject Avro._Schema Avro._Schema_union (wrap Avro._Union (var "decoded")))
(Eithers.mapList (decodeSchema @@ var "cx") (var "schemas")),
-- Object: named type or container
JM._Value_object>>: lambda "m" $
Eithers.bind (requireStringE @@ var "cx" @@ avro_type @@ var "m")
(lambda "typeName" $ decodeObjectSchema @@ var "cx" @@ var "m" @@ var "typeName")]
decodeObjectSchema :: TTermDefinition (Context -> M.Map String JM.Value -> String -> Result Avro.Schema)
decodeObjectSchema = define "decodeObjectSchema" $
doc "Decode an Avro schema from a JSON object given the type name" $
lambda "cx" $ lambda "m" $ lambda "typeName" $
Logic.ifElse (Equality.equal (var "typeName") (string "array"))
(decodeArraySchema @@ var "cx" @@ var "m")
(Logic.ifElse (Equality.equal (var "typeName") (string "map"))
(decodeMapSchema @@ var "cx" @@ var "m")
(Logic.ifElse (Equality.equal (var "typeName") (string "record"))
(decodeNamedSchema @@ var "cx" @@ var "m" @@ (decodeRecord @@ var "cx" @@ var "m"))
(Logic.ifElse (Equality.equal (var "typeName") (string "enum"))
(decodeNamedSchema @@ var "cx" @@ var "m" @@ (decodeEnum @@ var "cx" @@ var "m"))
(Logic.ifElse (Equality.equal (var "typeName") (string "fixed"))
(decodeNamedSchema @@ var "cx" @@ var "m" @@ (decodeFixed @@ var "cx" @@ var "m"))
-- Primitive type as object (unusual but valid, e.g. {"type": "string"})
(Maybes.maybe
(err @@ var "cx" @@ (Strings.cat $ list [string "unknown type: ", var "typeName"]))
(lambda "p" $ Phantoms.right (inject Avro._Schema Avro._Schema_primitive (var "p")))
(decodePrimitiveName @@ var "typeName"))))))
decodeArraySchema :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.Schema)
decodeArraySchema = define "decodeArraySchema" $
doc "Decode an Avro array schema from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireE @@ var "cx" @@ avro_items @@ var "m")
(lambda "items" $
Eithers.map
(lambda "s" $ inject Avro._Schema Avro._Schema_array (record Avro._Array [
Avro._Array_items>>: var "s"]))
(decodeSchema @@ var "cx" @@ var "items"))
decodeMapSchema :: TTermDefinition (Context -> M.Map String JM.Value -> Result Avro.Schema)
decodeMapSchema = define "decodeMapSchema" $
doc "Decode an Avro map schema from a JSON object map" $
lambda "cx" $ lambda "m" $
Eithers.bind (requireE @@ var "cx" @@ avro_values @@ var "m")
(lambda "values" $
Eithers.map
(lambda "s" $ inject Avro._Schema Avro._Schema_map (record Avro._Map [
Avro._Map_values>>: var "s"]))
(decodeSchema @@ var "cx" @@ var "values"))
getAnnotations :: TTermDefinition (M.Map String JM.Value -> M.Map String JM.Value)
getAnnotations = define "getAnnotations" $
doc "Extract annotation entries (keys starting with @) from a JSON object map" $
lambda "m" $
Maps.fromList (Maybes.cat (Lists.map
(lambda "entry" $ lets [
"k">: Pairs.first (var "entry"),
"v">: Pairs.second (var "entry")] $
Logic.ifElse
(Equality.equal (Maybes.fromMaybe (int32 0) (Strings.maybeCharAt (int32 0) (var "k"))) (int32 64)) -- 64 = '@'
(Maybes.pure (pair (Strings.fromList (Lists.drop (int32 1) (Strings.toList (var "k")))) (var "v")))
nothing)
(Maps.toList (var "m"))))