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]

Reply via email to