blob: 443b782249a550fbebd09c423423d6bdca078678 [file] [log] [blame]
/*
* 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;