tynder
Version:
TypeScript friendly Data validator for JavaScript.
236 lines (235 loc) • 8.95 kB
JavaScript
;
// Copyright (c) 2019 Shellyl_N and Authors
// license: ISC
// https://github.com/shellyln
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateProto3Code = void 0;
const escape_1 = require("../../lib/escape");
function formatTypeName(ty, ctx, typeName) {
if (typeName.includes('.')) {
return generateProto3CodeInner(ty, false, ctx);
}
return typeName;
}
function formatProto3CodeDocComment(ty, nestLevel) {
let code = '';
const indent = ' '.repeat(nestLevel);
const docComment = typeof ty === 'string' ? ty : ty.docComment;
if (docComment) {
if (0 <= docComment.indexOf('\n')) {
code += `${indent}/**\n${indent} ${docComment
.split('\n')
.map(x => x.trimLeft())
.join(`\n${indent} `)}\n${indent} */\n`;
}
else {
code += `${indent}/** ${docComment} */\n`;
}
}
return code;
}
function formatMemberType(ty, ctx) {
if (ty.typeName) {
return formatTypeName(ty, ctx, ty.typeName);
}
else {
switch (ty.kind) {
case 'primitive':
return generateProto3CodePrimitive(ty, ctx);
case 'primitive-value':
return generateProto3CodePrimitiveValue(ty, ctx);
case 'repeated':
return generateProto3CodeRepeated(ty, ctx);
case 'one-of':
return generateProto3CodeOneOf(ty, ctx);
default:
return 'object';
}
}
}
function appendOptionalModifier(name) {
switch (name) {
case 'double':
return 'google.protobuf.DoubleValue';
case 'int64':
return 'google.protobuf.Int64Value';
case 'int32':
return 'google.protobuf.Int32Value';
case 'string':
return 'google.protobuf.StringValue';
case 'bool':
return 'google.protobuf.BoolValue';
default:
return name;
}
}
function isNullableOneOf(ty, ctx) {
const filtered = ty.oneOf.filter(x => !(x.kind === 'primitive' && (x.primitiveName === 'null' || x.primitiveName === 'undefined') ||
x.kind === 'primitive-value' && (x.value === null || x.value === void 0)));
return (filtered.length === 1 && ty.oneOf.length !== 1 ? filtered[0] : null);
}
function generateProto3CodePrimitive(ty, ctx) {
switch (ty.primitiveName) {
case 'number':
return 'double';
case 'integer':
return 'int32';
case 'bigint':
return 'string';
case 'string':
return 'string';
case 'boolean':
return 'bool';
case 'undefined':
case 'null':
default:
return 'google.protobuf.Any';
}
// TODO: Function, integer, DateStr, DateTimeStr
}
function generateProto3CodePrimitiveValue(ty, ctx) {
if (ty.value === null) {
return 'google.protobuf.Any';
}
if (ty.value === void 0) {
return 'google.protobuf.Any';
}
switch (typeof ty.value) {
case 'number':
return 'double';
case 'bigint':
return 'string';
case 'string':
return 'string';
case 'boolean':
return 'bool';
default:
return 'google.protobuf.Any';
}
}
function generateProto3CodeRepeated(ty, ctx) {
return (`repeated ${ty.repeated.typeName ?
formatTypeName(ty.repeated, ctx, ty.repeated.typeName) :
ty.repeated.kind === 'repeated' ?
'google.protobuf.Any' :
generateProto3CodeInner(ty.repeated, false, ctx)}`);
}
function generateProto3CodeSpread(ty, ctx) {
return '';
}
function generateProto3CodeSequence(ty, ctx) {
return 'repeated google.protobuf.Any';
}
function generateProto3CodeOneOf(ty, ctx) {
const z = isNullableOneOf(ty, ctx);
if (z) {
return appendOptionalModifier(formatMemberType(z, ctx));
}
else {
return 'google.protobuf.Any';
}
}
function generateProto3CodeOptional(ty, ctx) {
return appendOptionalModifier(generateProto3CodeInner(ty.optional, false, ctx));
}
function generateProto3CodeEnum(ty, ctx) {
return (ty.typeName ?
formatTypeName(ty, ctx, ty.typeName) :
'google.protobuf.Any');
}
function generateProto3CodeObject(ty, isInterface, ctx) {
if (ty.members.length === 0) {
return '{}';
}
const sep = isInterface ? ';\n' : ',\n';
let count = 1;
const memberLines = ty.members
.map(x => `${formatProto3CodeDocComment(x[3] || '', ctx.nestLevel + 1)}${' '.repeat(ctx.nestLevel + 1)}${x[1].typeName ?
formatTypeName(x[1], Object.assign(Object.assign({}, ctx), { nestLevel: ctx.nestLevel + 1 }), x[1].typeName) :
generateProto3CodeInner(x[1], false, Object.assign(Object.assign({}, ctx), { nestLevel: ctx.nestLevel + 1 }))} ${x[0]} = ${count++}`);
return (`{\n${memberLines.join(sep)}${sep}${' '.repeat(ctx.nestLevel)}}`);
}
function generateProto3CodeInner(ty, isInterface, ctx) {
switch (ty.kind) {
case 'never':
case 'any':
case 'unknown':
return 'google.protobuf.Any';
case 'primitive':
return generateProto3CodePrimitive(ty, ctx);
case 'primitive-value':
return generateProto3CodePrimitiveValue(ty, ctx);
case 'repeated':
return generateProto3CodeRepeated(ty, ctx);
case 'spread':
return generateProto3CodeSpread(ty, ctx);
case 'sequence':
return generateProto3CodeSequence(ty, ctx);
case 'one-of':
return generateProto3CodeOneOf(ty, ctx);
case 'optional':
return generateProto3CodeOptional(ty, ctx);
case 'enum':
return generateProto3CodeEnum(ty, ctx);
case 'object':
return generateProto3CodeObject(ty, isInterface, ctx);
case 'symlink':
return ty.symlinkTargetName;
case 'operator':
throw new Error(`Unexpected type assertion: ${ty.kind}`);
default:
throw new Error(`Unknown type assertion: ${ty.kind}`);
}
}
function generateProto3Code(types) {
let code = `
syntax = "proto3";
import "google/protobuf/wrappers.proto";
import "google/protobuf/any.proto";
`;
const ctx = { nestLevel: 0 };
for (const ty of types.entries()) {
if (ty[1].ty.noOutput) {
const indent0 = ' '.repeat(ctx.nestLevel);
const indent1 = ' '.repeat(ctx.nestLevel + 1);
code += `message ${ty[0]} {\n${indent1}google.protobuf.Any value = 1;\n${indent0}}\n\n`;
continue;
}
code += formatProto3CodeDocComment(ty[1].ty, ctx.nestLevel);
if (ty[1].ty.kind === 'object') {
code += `message ${ty[0]} ${generateProto3CodeInner(ty[1].ty, true, ctx)}\n\n`;
}
else if (ty[1].ty.kind === 'enum') {
const indent0 = ' '.repeat(ctx.nestLevel);
const indent1 = ' '.repeat(ctx.nestLevel + 1);
if (0 < ty[1].ty.values.filter(x => typeof x[1] !== 'number').length) { // NOTE: string enum is not allowed
code += `message ${ty[0]} {\n${indent1}google.protobuf.Any value = 1;\n${indent0}}\n\n`;
}
else {
code += `enum ${ty[0]} {\n${indent1}option allow_alias = true;\n${ty[1].ty.values.filter(x => x[1] === 0).length === 0 ?
`${indent1}${ty[0]}__UNKNOWN__ = 0;\n` : // NOTE: 0 value item is required
''}${ty[1].ty.values
.map(x => `${formatProto3CodeDocComment(x[2] || '', ctx.nestLevel + 1)}${indent1}${(() => {
if (typeof x[1] === 'number') {
return `${ty[0]}_${x[0]} = ${x[1]}`; // NOTE: label namespace is shared by all top-level enum
}
else {
return `${ty[0]}_${x[0]} = '${escape_1.escapeString(x[1])}'`; // NOTE: string enum is not allowed
}
})()};\n`)
.join('')}${indent0}}\n\n`;
}
}
else if (ty[1].ty.kind === 'never' && ty[1].ty.passThruCodeBlock) {
// nothing to do
}
else {
const indent0 = ' '.repeat(ctx.nestLevel);
const indent1 = ' '.repeat(ctx.nestLevel + 1);
code += `message ${ty[0]} {\n${indent1}${generateProto3CodeInner(ty[1].ty, false, ctx)} value = 1;\n${indent0}}\n\n`;
}
}
return code;
}
exports.generateProto3Code = generateProto3Code;
//# sourceMappingURL=proto3.js.map