This is an automated email from the ASF dual-hosted git repository.
chaokunyang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-fury.git
The following commit(s) were added to refs/heads/main by this push:
new a07bf8c2 feat(JavaScript): Implement Enum (#1321)
a07bf8c2 is described below
commit a07bf8c2a9b34e962f548f7ec34812a96bdf51e9
Author: weipeng <[email protected]>
AuthorDate: Mon Jan 8 17:53:29 2024 +0800
feat(JavaScript): Implement Enum (#1321)
Implement the Enum type which declare in SPEC.
Usage:
```TypeScript
enum Foo {
f1 = 1,
f2 = 2
}
const fury = new Fury({ refTracking: true });
const {serialize, deserialize} = fury.registerSerializer(Type.enum(Foo))
const input = serialize(Foo.f1);
const result = deserialize(
input
);
expect(result).toEqual(Foo.f1)
```
---
javascript/packages/fury/lib/description.ts | 29 +++++++++-
javascript/packages/fury/lib/gen/enum.ts | 88 +++++++++++++++++++++++++++++
javascript/packages/fury/lib/gen/index.ts | 1 +
javascript/packages/fury/lib/meta.ts | 5 ++
javascript/packages/fury/lib/type.ts | 4 +-
javascript/test/enum.test.ts | 80 ++++++++++++++++++++++++++
6 files changed, 205 insertions(+), 2 deletions(-)
diff --git a/javascript/packages/fury/lib/description.ts
b/javascript/packages/fury/lib/description.ts
index 832a1214..42855e4f 100644
--- a/javascript/packages/fury/lib/description.ts
+++ b/javascript/packages/fury/lib/description.ts
@@ -31,6 +31,12 @@ export interface ObjectTypeDescription extends
TypeDescription {
}
}
+export interface EnumTypeDescription extends TypeDescription {
+ options: {
+ inner: { [key: string]: any }
+ }
+}
+
export interface ArrayTypeDescription extends TypeDescription {
options: {
inner: TypeDescription
@@ -92,6 +98,16 @@ type TupleProps<T> = T extends {
? { [K in keyof T2]: ToRecordType<T2[K]> }
: unknown;
+type Value<T> = T extends { [s: string]: infer T2 } ? T2 : unknown;
+
+type EnumProps<T> = T extends {
+ options: {
+ inner: infer T2
+ }
+}
+ ? Value<T2>
+ : unknown;
+
type SetProps<T> = T extends {
options: {
key: infer T2 extends TypeDescription
@@ -162,7 +178,10 @@ export type ToRecordType<T> = T extends {
type: InternalSerializerType.ANY
}
? any
- : unknown;
+ : T extends {
+ type: InternalSerializerType.ENUM
+ }
+ ? EnumProps<T> : unknown;
export const Type = {
any() {
@@ -170,6 +189,14 @@ export const Type = {
type: InternalSerializerType.ANY as const,
};
},
+ enum<T1 extends { [key: string]: any }>(t1: T1) {
+ return {
+ type: InternalSerializerType.ENUM as const,
+ options: {
+ inner: t1,
+ },
+ };
+ },
string() {
return {
type: InternalSerializerType.STRING as const,
diff --git a/javascript/packages/fury/lib/gen/enum.ts
b/javascript/packages/fury/lib/gen/enum.ts
new file mode 100644
index 00000000..7237b2ba
--- /dev/null
+++ b/javascript/packages/fury/lib/gen/enum.ts
@@ -0,0 +1,88 @@
+/*
+ * 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.
+ */
+
+import { EnumTypeDescription, TypeDescription } from "../description";
+import { CodecBuilder } from "./builder";
+import { BaseSerializerGenerator } from "./serializer";
+import { CodegenRegistry } from "./router";
+import { InternalSerializerType, MaxUInt32 } from "../type";
+import { Scope } from "./scope";
+
+class EnumSerializerGenerator extends BaseSerializerGenerator {
+ description: EnumTypeDescription;
+
+ constructor(description: TypeDescription, builder: CodecBuilder, scope:
Scope) {
+ super(description, builder, scope);
+ this.description = <EnumTypeDescription>description;
+ }
+
+ writeStmt(accessor: string): string {
+ if (Object.values(this.description.options.inner).length < 1) {
+ throw new Error("An enum must contain at least one field");
+ }
+ return `
+ ${Object.values(this.description.options.inner).map((value, index) => {
+ if (typeof value !== "string" && typeof value !== "number") {
+ throw new Error("Enum value must be string or number");
+ }
+ if (typeof value === "number") {
+ if (value > MaxUInt32 || value < 0) {
+ throw new Error("Enum value must be a valid uint32");
+ }
+ }
+ const safeValue = typeof value === "string" ? `"${value}"` : value;
+ return ` if (${accessor} === ${safeValue}) {
+ ${this.builder.writer.varUInt32(index)}
+ }`;
+ }).join(" else ")}
+ else {
+ throw new Error("Enum received an unexpected value: " +
${accessor});
+ }
+ `;
+ }
+
+ readStmt(accessor: (expr: string) => string): string {
+ const enumValue = this.scope.uniqueName("enum_v");
+ return `
+ const ${enumValue} = ${this.builder.reader.varUInt32()};
+ switch(${enumValue}) {
+ ${Object.values(this.description.options.inner).map((value, index)
=> {
+ if (typeof value !== "string" && typeof value !== "number") {
+ throw new Error("Enum value must be string or number");
+ }
+ if (typeof value === "number") {
+ if (value > MaxUInt32 || value < 0) {
+ throw new Error("Enum value must be a valid uint32");
+ }
+ }
+ const safeValue = typeof value === "string" ? `"${value}"` :
`${value}`;
+ return `
+ case ${index}:
+ ${accessor(safeValue)}
+ break;
+ `;
+ }).join("\n")}
+ default:
+ throw new Error("Enum received an unexpected value: " +
enumValue);
+ }
+ `;
+ }
+}
+
+CodegenRegistry.register(InternalSerializerType.ENUM, EnumSerializerGenerator);
diff --git a/javascript/packages/fury/lib/gen/index.ts
b/javascript/packages/fury/lib/gen/index.ts
index 84f60aa0..1a347bc1 100644
--- a/javascript/packages/fury/lib/gen/index.ts
+++ b/javascript/packages/fury/lib/gen/index.ts
@@ -34,6 +34,7 @@ import "./set";
import "./any";
import "./tuple";
import "./typedArray";
+import "./enum";
export const generate = (fury: Fury, description: TypeDescription) => {
const InnerGeneratorClass = CodegenRegistry.get(description.type);
diff --git a/javascript/packages/fury/lib/meta.ts
b/javascript/packages/fury/lib/meta.ts
index 69a7ac88..eb81bd5d 100644
--- a/javascript/packages/fury/lib/meta.ts
+++ b/javascript/packages/fury/lib/meta.ts
@@ -137,6 +137,11 @@ export const getMeta = (description: TypeDescription,
fury: Fury): Meta<any> =>
fixedSize: 11,
noneable: true,
};
+ case InternalSerializerType.ENUM:
+ return {
+ fixedSize: 7,
+ noneable: true,
+ };
default:
throw new Error(`Meta of ${description.type} not exists`);
}
diff --git a/javascript/packages/fury/lib/type.ts
b/javascript/packages/fury/lib/type.ts
index 2a1def72..fe68a127 100644
--- a/javascript/packages/fury/lib/type.ts
+++ b/javascript/packages/fury/lib/type.ts
@@ -45,6 +45,7 @@ export enum InternalSerializerType {
BINARY = 14,
DATE = 16,
TIMESTAMP = 18,
+ ENUM = 19, // The cross-language enum has not yet been determined, this is
not the final value, it will change later
FURY_TYPE_TAG = 256,
FURY_SET = 257,
FURY_PRIMITIVE_BOOL_ARRAY = 258,
@@ -86,7 +87,8 @@ export enum RefFlags {
export const MaxInt32 = 2147483647;
export const MinInt32 = -2147483648;
-
+export const MaxUInt32 = 0xFFFFFFFF;
+export const MinUInt32 = 0;
export const HalfMaxInt32 = MaxInt32 / 2;
export const HalfMinInt32 = MinInt32 / 2;
diff --git a/javascript/test/enum.test.ts b/javascript/test/enum.test.ts
new file mode 100644
index 00000000..86a9cb38
--- /dev/null
+++ b/javascript/test/enum.test.ts
@@ -0,0 +1,80 @@
+/*
+ * 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.
+ */
+
+import Fury, { TypeDescription, InternalSerializerType, Type } from
'../packages/fury/index';
+import {describe, expect, test} from '@jest/globals';
+
+describe('enum', () => {
+ test('should javascript number enum work', () => {
+ const Foo = {
+ f1: 1,
+ f2: 2
+ }
+ const fury = new Fury({ refTracking: true });
+ const {serialize, deserialize} =
fury.registerSerializer(Type.enum(Foo))
+ const input = serialize(Foo.f1);
+ const result = deserialize(
+ input
+ );
+ expect(result).toEqual(Foo.f1)
+ });
+
+ test('should javascript string enum work', () => {
+ const Foo = {
+ f1: "hello",
+ f2: "world"
+ }
+ const fury = new Fury({ refTracking: true });
+ fury.registerSerializer(Type.enum(Foo))
+ const input = fury.serialize(Foo.f1);
+ const result = fury.deserialize(
+ input
+ );
+ expect(result).toEqual(Foo.f1)
+ });
+ test('should typescript number enum work', () => {
+ enum Foo {
+ f1 = 1,
+ f2 = 2
+ }
+ const fury = new Fury({ refTracking: true });
+ const {serialize, deserialize} = fury.registerSerializer(Type.enum(Foo))
+ const input = serialize(Foo.f1);
+ const result = deserialize(
+ input
+ );
+ expect(result).toEqual(Foo.f1)
+ });
+
+ test('should typescript string enum work', () => {
+ enum Foo {
+ f1 = "hello",
+ f2 = "world"
+ }
+ const fury = new Fury({ refTracking: true });
+ fury.registerSerializer(Type.enum(Foo))
+ const input = fury.serialize(Foo.f1);
+ const result = fury.deserialize(
+ input
+ );
+ expect(result).toEqual(Foo.f1)
+ });
+});
+
+
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]