blob: 5e49878a049835a6eecab1dcd06fd5afb0954ba2 [file]
/*
* 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 { TypeId, Serializer } from "../type";
import { TypeInfo } from "../typeInfo";
import { CodegenRegistry } from "./router";
import { CodecBuilder } from "./builder";
import { Scope } from "./scope";
import "./array";
import "./struct";
import "./string";
import "./bool";
import "./datetime";
import "./decimal";
import "./map";
import "./number";
import "./set";
import "./struct";
import "./typedArray";
import "./enum";
import "./any";
import "./union";
import "./ext";
import TypeResolver from "../typeResolver";
export class Gen {
static external = CodegenRegistry.getExternal();
constructor(private typeResolver: TypeResolver, private regOptions: { [key: string]: any } = {}) {
}
private generate(typeInfo: TypeInfo) {
const InnerGeneratorClass = CodegenRegistry.get(typeInfo.typeId);
if (!InnerGeneratorClass) {
throw new Error(`${typeInfo.typeId} generator not exists`);
}
const scope = new Scope();
const generator = new InnerGeneratorClass(typeInfo, new CodecBuilder(scope, this.typeResolver), scope);
const funcString = generator.toSerializer();
if (this.typeResolver.config && this.typeResolver.config.hooks) {
const afterCodeGenerated = this.typeResolver.config.hooks.afterCodeGenerated;
if (typeof afterCodeGenerated === "function") {
return new Function(afterCodeGenerated(funcString));
}
}
return new Function(funcString);
}
private register(typeInfo: TypeInfo, serializer?: Serializer) {
this.typeResolver.registerSerializer(typeInfo, serializer);
}
private isRegistered(typeInfo: TypeInfo) {
return !!this.typeResolver.getSerializerByTypeInfo(typeInfo);
}
private isFullyGenerated(typeInfo: TypeInfo) {
const ser = this.typeResolver.getSerializerByTypeInfo(typeInfo);
return ser && ser._initialized;
}
private traversalContainer(typeInfo: TypeInfo) {
if (TypeId.userDefinedType(typeInfo.typeId)) {
if (this.isFullyGenerated(typeInfo)) {
return;
}
const options = (typeInfo).options;
if (options?.props && Object.keys(options.props).length > 0) {
this.register(typeInfo);
Object.values(options.props).forEach((x) => {
this.traversalContainer(x);
});
const func = this.generate(typeInfo);
this.register(typeInfo, func()(this.typeResolver, Gen.external, typeInfo, this.regOptions));
} else if (!this.isRegistered(typeInfo) && TypeId.structType(typeInfo.typeId)) {
// Forward reference to a struct type not yet fully defined — register a
// placeholder so that serializer factories can capture the object
// reference. The placeholder will be filled in via Object.assign
// when the real serializer is generated later.
this.register(typeInfo);
}
}
if (typeInfo.typeId === TypeId.LIST) {
this.traversalContainer(typeInfo.options!.inner!);
}
if (typeInfo.typeId === TypeId.SET) {
this.traversalContainer((typeInfo).options!.key!);
}
if (typeInfo.typeId === TypeId.MAP) {
if (!typeInfo.options?.key || !typeInfo.options?.value) {
throw new Error("map type must have key and value");
}
this.traversalContainer((typeInfo).options!.key!);
this.traversalContainer((typeInfo).options!.value!);
}
if (typeInfo.options?.cases) {
Object.values(typeInfo.options.cases).forEach((caseTypeInfo) => {
this.traversalContainer(caseTypeInfo);
});
}
}
reGenerateSerializer(typeInfo: TypeInfo) {
const func = this.generate(typeInfo);
return func()(this.typeResolver, Gen.external, typeInfo, this.regOptions);
}
generateSerializer(typeInfo: TypeInfo) {
this.traversalContainer(typeInfo);
const serializer = this.typeResolver.getSerializerByTypeInfo(typeInfo);
if (serializer?._initialized) {
return serializer;
}
return this.reGenerateSerializer(typeInfo);
}
}