| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| "use strict"; |
| |
| import {Type} from "./core"; |
| |
| import "./generated/types_pb"; |
| import "./generated/request-reply_pb"; |
| |
| // primitive type names |
| |
| const BOOLEAN_TYPENAME = "io.statefun.types/bool"; |
| const INTEGER_TYPENAME = "io.statefun.types/int"; |
| const FLOAT_TYPENAME = "io.statefun.types/float"; |
| const STRING_TYPENAME = "io.statefun.types/string"; |
| |
| // The following types are simply not supported in JavaScript. |
| // TODO: should we use BigNums here? |
| // const LONG_TYPENAME = "io.statefun.types/long"; |
| // const DOUBLE_TYPENAME = "io.statefun.types/double"; |
| |
| // noinspection JSValidateJSDoc |
| export class TypedValueSupport { |
| |
| /** |
| * Parse an instance of a TypedValue via the given type to a JS Object. |
| * |
| * @param {proto.io.statefun.sdk.reqreply.TypedValue} box |
| * @param {Type} type an StateFun type |
| * @returns {null|any} a JsObject that was via type or NULL if the typed value was empty. |
| */ |
| static parseTypedValue<T>( |
| box: proto.io.statefun.sdk.reqreply.TypedValue | undefined | null, |
| type: Type<T> |
| ): T | null { |
| if (type === undefined || type === null) { |
| throw new Error("Type can not be missing"); |
| } |
| if (box === undefined || box === null) { |
| return null; |
| } |
| if (!box.getHasValue()) { |
| return null; |
| } |
| if (box.getTypename() !== type.typename) { |
| throw new Error(`Type mismatch: ${box.getTypename()} is not of type ${type.typename}`); |
| } |
| const buf = Buffer.from(box.getValue_asU8()); |
| return type.deserialize(buf); |
| } |
| |
| static toTypedValue<T>(obj: T, type: Type<T>): proto.io.statefun.sdk.reqreply.TypedValue { |
| if (type === undefined || type === null) { |
| throw new Error("Type can not be missing"); |
| } |
| const ret = new proto.io.statefun.sdk.reqreply.TypedValue(); |
| ret.setTypename(type.typename); |
| |
| if (obj === undefined || obj === null) { |
| ret.setHasValue(false); |
| } else { |
| ret.setHasValue(true); |
| // serialize |
| const buffer = type.serialize(obj); |
| ret.setValue(buffer); |
| } |
| return ret; |
| } |
| |
| static toTypedValueRaw(typename: string, bytes: Buffer | Uint8Array): proto.io.statefun.sdk.reqreply.TypedValue { |
| const box = new proto.io.statefun.sdk.reqreply.TypedValue(); |
| box.setHasValue(true); |
| box.setTypename(typename); |
| box.setValue(bytes); |
| return box; |
| } |
| |
| } |
| |
| // primitive types |
| |
| class ProtobufWrapperType<T> extends Type<T> { |
| readonly #wrapper: any; |
| |
| constructor(typename: string, wrapper: any) { |
| super(typename); |
| this.#wrapper = wrapper; |
| } |
| |
| serialize(value: T) { |
| const wrapper = new this.#wrapper(); |
| wrapper.setValue(value); |
| return wrapper.serializeBinary(); |
| } |
| |
| deserialize(bytes: Buffer): T { |
| const wrapper = this.#wrapper.deserializeBinary(bytes); |
| return wrapper.getValue(); |
| } |
| } |
| |
| class BoolType extends ProtobufWrapperType<boolean> { |
| static readonly INSTANCE = new BoolType(); |
| |
| constructor() { |
| super(BOOLEAN_TYPENAME, global.proto.io.statefun.sdk.types.BooleanWrapper); |
| } |
| } |
| |
| class IntType extends ProtobufWrapperType<number> { |
| static readonly INSTANCE = new IntType(); |
| |
| constructor() { |
| super(INTEGER_TYPENAME, proto.io.statefun.sdk.types.IntWrapper); |
| } |
| } |
| |
| // noinspection JSUnresolvedVariable |
| class FloatType extends ProtobufWrapperType<number> { |
| static readonly INSTANCE = new FloatType(); |
| |
| constructor() { |
| super(FLOAT_TYPENAME, proto.io.statefun.sdk.types.FloatWrapper); |
| } |
| } |
| |
| class StringType extends ProtobufWrapperType<string> { |
| static readonly INSTANCE = new StringType(); |
| |
| constructor() { |
| super(STRING_TYPENAME, proto.io.statefun.sdk.types.StringWrapper); |
| } |
| } |
| |
| export class CustomType<T> extends Type<T> { |
| readonly #serializer: (a: T) => Buffer; |
| readonly #deserializer: (buf: Buffer) => T; |
| |
| constructor(typename: string, serialize: (a: T) => Buffer, deserializer: (buf: Buffer) => T) { |
| super(typename); |
| this.#serializer = serialize; |
| this.#deserializer = deserializer; |
| } |
| |
| serialize(value: T) { |
| return this.#serializer(value); |
| } |
| |
| deserialize(bytes: Buffer) { |
| return this.#deserializer(bytes); |
| } |
| } |
| |
| export class JsonType<T> extends Type<T> { |
| |
| constructor(typename: string) { |
| super(typename); |
| } |
| |
| serialize(object: T) { |
| return Buffer.from(JSON.stringify(object)); |
| } |
| |
| deserialize(buffer: Buffer): T { |
| return JSON.parse(buffer.toString()); |
| } |
| } |
| |
| export class ProtobufType<T> extends Type<T> { |
| #wrapper: any; |
| |
| constructor(typename: string, wrapper: any) { |
| super(typename); |
| this.#wrapper = wrapper; |
| } |
| |
| serialize(value: any): Buffer { |
| return value.serializeBinary(); |
| } |
| |
| deserialize(bytes: Buffer) { |
| return this.#wrapper.deserializeBinary(bytes); |
| } |
| } |
| |
| export const BOOL_TYPE = BoolType.INSTANCE; |
| export const INT_TYPE = IntType.INSTANCE; |
| export const FLOAT_TYPE = FloatType.INSTANCE; |
| export const STRING_TYPE = StringType.INSTANCE; |