hydra-kernel-0.16.0: src/main/haskell/Hydra/Json/Encode.hs
-- Note: this is an automatically generated file. Do not edit.
-- | JSON encoding for Hydra terms. Converts Terms to JSON Values using Either for error handling.
module Hydra.Json.Encode where
import qualified Hydra.Ast as Ast
import qualified Hydra.Coders as Coders
import qualified Hydra.Core as Core
import qualified Hydra.Error.Checking as Checking
import qualified Hydra.Error.Core as ErrorCore
import qualified Hydra.Error.Packaging as ErrorPackaging
import qualified Hydra.Errors as Errors
import qualified Hydra.Extract.Core as ExtractCore
import qualified Hydra.Graph as Graph
import qualified Hydra.Json.Model as Model
import qualified Hydra.Haskell.Lib.Eithers as Eithers
import qualified Hydra.Haskell.Lib.Equality as Equality
import qualified Hydra.Haskell.Lib.Lists as Lists
import qualified Hydra.Haskell.Lib.Literals as LibLiterals
import qualified Hydra.Haskell.Lib.Logic as Logic
import qualified Hydra.Haskell.Lib.Maps as Maps
import qualified Hydra.Haskell.Lib.Optionals as Optionals
import qualified Hydra.Haskell.Lib.Pairs as Pairs
import qualified Hydra.Haskell.Lib.Sets as Sets
import qualified Hydra.Haskell.Lib.Strings as Strings
import qualified Hydra.Literals as Literals
import qualified Hydra.Packaging as Packaging
import qualified Hydra.Parsing as Parsing
import qualified Hydra.Paths as Paths
import qualified Hydra.Query as Query
import qualified Hydra.Relational as Relational
import qualified Hydra.Show.Core as ShowCore
import qualified Hydra.Strip as Strip
import qualified Hydra.Tabular as Tabular
import qualified Hydra.Testing as Testing
import qualified Hydra.Topology as Topology
import qualified Hydra.Typed as Typed
import qualified Hydra.Typing as Typing
import qualified Hydra.Util as Util
import qualified Hydra.Validation as Validation
import qualified Hydra.Variants as Variants
import Prelude hiding (Enum, Ordering, decodeFloat, encodeFloat, fail, map, pure, sum)
import qualified Data.Scientific as Sci
import qualified Data.Map as M
-- | Encode a float value to JSON. Finite values become JSON numbers (shortest round-trip); IEEE specials (NaN/Inf/-0.0) become JSON strings. Float32 and Float64 are symmetric; the schema disambiguates precision on decode.
encodeFloat :: Core.FloatValue -> Either t0 Model.Value
encodeFloat fv =
case fv of
Core.FloatValueFloat32 v0 ->
let s = LibLiterals.showFloat32 v0
in (Logic.ifElse (requiresJsonStringSentinel s) (Right (Model.ValueString s)) (Right (Model.ValueNumber (LibLiterals.float32ToDecimal v0))))
Core.FloatValueFloat64 v0 ->
let s = LibLiterals.showFloat64 v0
in (Logic.ifElse (requiresJsonStringSentinel s) (Right (Model.ValueString s)) (Right (Model.ValueNumber (LibLiterals.float64ToDecimal v0))))
-- | Encode an integer value to JSON. Small ints use native numbers; large ints use strings.
encodeInteger :: Core.IntegerValue -> Either t0 Model.Value
encodeInteger iv =
case iv of
Core.IntegerValueBigint v0 -> Right (Model.ValueString (LibLiterals.showBigint v0))
Core.IntegerValueInt64 v0 -> Right (Model.ValueString (LibLiterals.showInt64 v0))
Core.IntegerValueUint64 v0 -> Right (Model.ValueString (LibLiterals.showUint64 v0))
Core.IntegerValueInt8 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.int8ToBigint v0)))
Core.IntegerValueInt16 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.int16ToBigint v0)))
Core.IntegerValueInt32 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.int32ToBigint v0)))
Core.IntegerValueUint8 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.uint8ToBigint v0)))
Core.IntegerValueUint16 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.uint16ToBigint v0)))
Core.IntegerValueUint32 v0 -> Right (Model.ValueNumber (LibLiterals.bigintToDecimal (LibLiterals.uint32ToBigint v0)))
-- | Encode a Hydra literal to a JSON value
encodeLiteral :: Core.Literal -> Either t0 Model.Value
encodeLiteral lit =
case lit of
Core.LiteralBinary v0 -> Right (Model.ValueString (LibLiterals.binaryToString v0))
Core.LiteralBoolean v0 -> Right (Model.ValueBoolean v0)
Core.LiteralDecimal v0 -> Right (Model.ValueNumber v0)
Core.LiteralFloat v0 -> encodeFloat v0
Core.LiteralInteger v0 -> encodeInteger v0
Core.LiteralString v0 -> Right (Model.ValueString v0)
-- | True for IEEE sentinel strings that JSON must escape as a string to preserve.
requiresJsonStringSentinel :: String -> Bool
requiresJsonStringSentinel s =
Logic.or (Equality.equal s "NaN") (Logic.or (Equality.equal s "Infinity") (Logic.or (Equality.equal s "-Infinity") (Equality.equal s "-0.0")))
-- | Encode a Hydra term to a JSON value given a type and type name. Returns Left for unsupported constructs.
toJson :: M.Map Core.Name Core.Type -> Core.Name -> Core.Type -> Core.Term -> Either String Model.Value
toJson types tname typ term =
let stripped = Strip.deannotateType typ
strippedTerm = Strip.deannotateTerm term
in case stripped of
Core.TypeLiteral _ -> case strippedTerm of
Core.TermLiteral v1 -> encodeLiteral v1
_ -> Left "expected literal term"
Core.TypeList v0 -> case strippedTerm of
Core.TermList v1 ->
let results = Eithers.mapList (\t -> toJson types tname v0 t) v1
in (Eithers.map (\vs -> Model.ValueArray vs) results)
_ -> Left "expected list term"
Core.TypeSet v0 -> case strippedTerm of
Core.TermSet v1 ->
let terms = Sets.toList v1
results = Eithers.mapList (\t -> toJson types tname v0 t) terms
in (Eithers.map (\vs -> Model.ValueArray vs) results)
_ -> Left "expected set term"
Core.TypeOptional v0 ->
let innerStripped = Strip.deannotateType v0
isNestedMaybe =
case innerStripped of
Core.TypeOptional _ -> True
_ -> False
in case strippedTerm of
Core.TermOptional v1 -> Optionals.cases v1 (Right Model.ValueNull) (\v ->
let encoded = toJson types tname v0 v
in (Logic.ifElse isNestedMaybe (Eithers.map (\ev -> Model.ValueArray [
ev]) encoded) encoded))
_ -> Left "expected maybe term"
Core.TypeRecord v0 -> case strippedTerm of
Core.TermRecord v1 ->
let isSimpleMaybe =
\ftype -> case (Strip.deannotateType ftype) of
Core.TypeOptional v2 -> case (Strip.deannotateType v2) of
Core.TypeOptional _ -> False
_ -> True
_ -> False
encodeFieldWithType =
\ft -> \f ->
let fname = Core.unName (Core.fieldName f)
fterm = Core.fieldTerm f
ftype = Core.fieldTypeType ft
in (Logic.ifElse (isSimpleMaybe ftype) (case (Strip.deannotateTerm fterm) of
Core.TermOptional v2 -> Optionals.cases v2 (Right Nothing) (\v ->
let innerType =
case (Strip.deannotateType ftype) of
Core.TypeOptional v3 -> v3
_ -> ftype
encoded = toJson types tname innerType v
in (Eithers.map (\ev -> Just (fname, ev)) encoded))
_ -> Left "expected maybe term for optional field") (
let encoded = toJson types tname ftype fterm
in (Eithers.map (\ev -> Just (fname, ev)) encoded)))
fieldTypes = v0
fields = Core.recordFields v1
encodedPairs =
Eithers.mapList (\ftf -> encodeFieldWithType (Pairs.first ftf) (Pairs.second ftf)) (Lists.zip fieldTypes fields)
in (Eithers.map (\pairs -> Model.ValueObject (Optionals.cat pairs)) encodedPairs)
_ -> Left "expected record term"
Core.TypeUnion v0 -> case strippedTerm of
Core.TermInject v1 ->
let field = Core.injectionField v1
fname = Core.unName (Core.fieldName field)
fterm = Core.fieldTerm field
ftypeResult =
Optionals.cases (Lists.find (\ft -> Equality.equal (Core.unName (Core.fieldTypeName ft)) fname) v0) (Left (Strings.cat [
"unknown variant: ",
fname])) (\ft -> Right (Core.fieldTypeType ft))
in (Eithers.either (\err -> Left err) (\ftype ->
let encodedUnion = toJson types tname ftype fterm
in (Eithers.map (\v -> Model.ValueObject [
(fname, v)]) encodedUnion)) ftypeResult)
_ -> Left "expected union term"
Core.TypeUnit -> Right (Model.ValueObject [])
Core.TypeWrap v0 -> case strippedTerm of
Core.TermWrap v1 -> toJson types tname v0 (Core.wrappedTermBody v1)
_ -> Left "expected wrapped term"
Core.TypeMap v0 ->
let keyType = Core.mapTypeKeys v0
valType = Core.mapTypeValues v0
in case strippedTerm of
Core.TermMap v1 ->
let encodeEntry =
\kv ->
let k = Pairs.first kv
v = Pairs.second kv
encodedK = toJson types tname keyType k
encodedV = toJson types tname valType v
in (Eithers.either (\err -> Left err) (\ek -> Eithers.map (\ev -> Model.ValueObject [
("key", ek),
("value", ev)]) encodedV) encodedK)
entries = Eithers.mapList encodeEntry (Maps.toList v1)
in (Eithers.map (\es -> Model.ValueArray es) entries)
_ -> Left "expected map term"
Core.TypePair v0 ->
let firstType = Core.pairTypeFirst v0
secondType = Core.pairTypeSecond v0
in case strippedTerm of
Core.TermPair v1 ->
let first = Pairs.first v1
second = Pairs.second v1
encodedFirst = toJson types tname firstType first
encodedSecond = toJson types tname secondType second
in (Eithers.either (\err -> Left err) (\ef -> Eithers.map (\es -> Model.ValueObject [
("first", ef),
("second", es)]) encodedSecond) encodedFirst)
_ -> Left "expected pair term"
Core.TypeEither v0 ->
let leftType = Core.eitherTypeLeft v0
rightType = Core.eitherTypeRight v0
in case strippedTerm of
Core.TermEither v1 -> Eithers.either (\l ->
let encodedL = toJson types tname leftType l
in (Eithers.map (\v -> Model.ValueObject [
("left", v)]) encodedL)) (\r ->
let encodedR = toJson types tname rightType r
in (Eithers.map (\v -> Model.ValueObject [
("right", v)]) encodedR)) v1
_ -> Left "expected either term"
Core.TypeVariable v0 ->
let lookedUp = Maps.lookup v0 types
in (Optionals.cases lookedUp (toJsonUntyped term) (\resolvedType -> toJson types v0 resolvedType term))
_ -> Left (Strings.cat [
"unsupported type for JSON encoding: ",
(ShowCore.type_ typ)])
-- | Encode a Hydra term to a JSON value without type information. Falls back to array-wrapped Maybe encoding.
toJsonUntyped :: Core.Term -> Either String Model.Value
toJsonUntyped term =
let stripped = Strip.deannotateTerm term
in case stripped of
Core.TermLiteral v0 -> encodeLiteral v0
Core.TermList v0 ->
let results = Eithers.mapList (\t -> toJsonUntyped t) v0
in (Eithers.map (\vs -> Model.ValueArray vs) results)
Core.TermSet v0 ->
let terms = Sets.toList v0
results = Eithers.mapList (\t -> toJsonUntyped t) terms
in (Eithers.map (\vs -> Model.ValueArray vs) results)
Core.TermOptional v0 -> Optionals.cases v0 (Right Model.ValueNull) (\v ->
let encodedMaybe = toJsonUntyped v
in (Eithers.map (\encoded -> Model.ValueArray [
encoded]) encodedMaybe))
Core.TermRecord v0 ->
let encodeField =
\f ->
let fname = Core.unName (Core.fieldName f)
fterm = Core.fieldTerm f
encodedField = toJsonUntyped fterm
in (Eithers.map (\v -> (fname, v)) encodedField)
fields = Core.recordFields v0
encodedFields = Eithers.mapList encodeField fields
in (Eithers.map (\fs -> Model.ValueObject fs) encodedFields)
Core.TermInject v0 ->
let field = Core.injectionField v0
fname = Core.unName (Core.fieldName field)
fterm = Core.fieldTerm field
encodedUnion = toJsonUntyped fterm
in (Eithers.map (\v -> Model.ValueObject [
(fname, v)]) encodedUnion)
Core.TermUnit -> Right (Model.ValueObject [])
Core.TermWrap v0 -> toJsonUntyped (Core.wrappedTermBody v0)
Core.TermMap v0 ->
let encodeEntry =
\kv ->
let k = Pairs.first kv
v = Pairs.second kv
encodedK = toJsonUntyped k
encodedV = toJsonUntyped v
in (Eithers.either (\err -> Left err) (\ek -> Eithers.map (\ev -> Model.ValueObject [
("key", ek),
("value", ev)]) encodedV) encodedK)
entries = Eithers.mapList encodeEntry (Maps.toList v0)
in (Eithers.map (\es -> Model.ValueArray es) entries)
Core.TermPair v0 ->
let first = Pairs.first v0
second = Pairs.second v0
encodedFirst = toJsonUntyped first
encodedSecond = toJsonUntyped second
in (Eithers.either (\err -> Left err) (\ef -> Eithers.map (\es -> Model.ValueObject [
("first", ef),
("second", es)]) encodedSecond) encodedFirst)
Core.TermEither v0 -> Eithers.either (\l ->
let encodedL = toJsonUntyped l
in (Eithers.map (\v -> Model.ValueObject [
("left", v)]) encodedL)) (\r ->
let encodedR = toJsonUntyped r
in (Eithers.map (\v -> Model.ValueObject [
("right", v)]) encodedR)) v0
_ -> Left (Strings.cat [
"unsupported term variant for JSON encoding: ",
(ShowCore.term term)])