capnpc-ts
Version:
Cap'n Proto schema compiler for TypeScript.
159 lines (115 loc) • 4.73 kB
text/typescript
import * as capnp from "capnp-ts";
import * as s from "capnp-ts/src/std/schema.capnp.js";
import { format } from "capnp-ts/src/util";
import initTrace from "debug";
import { CodeGeneratorFileContext } from "./code-generator-file-context";
import { ConcreteListType } from "./constants";
import * as E from "./errors";
import * as util from "./util";
const trace = initTrace("capnpc:file");
trace("load");
export function compareCodeOrder(a: { getCodeOrder(): number }, b: { getCodeOrder(): number }): number {
return a.getCodeOrder() - b.getCodeOrder();
}
export function getConcreteListType(ctx: CodeGeneratorFileContext, type: s.Type): string {
if (!type.isList()) return getJsType(ctx, type, false);
const elementType = type.getList().getElementType();
const elementTypeWhich = elementType.which();
if (elementTypeWhich === s.Type.LIST) {
return `capnp.PointerList(${getConcreteListType(ctx, elementType)})`;
} else if (elementTypeWhich === s.Type.STRUCT) {
const structNode = lookupNode(ctx, elementType.getStruct().getTypeId());
if (structNode.getStruct().getPreferredListEncoding() !== s.ElementSize.INLINE_COMPOSITE) {
throw new Error(E.GEN_FIELD_NON_INLINE_STRUCT_LIST);
}
return `capnp.CompositeList(${getJsType(ctx, elementType, false)})`;
}
return ConcreteListType[elementTypeWhich];
}
export function getDisplayNamePrefix(node: s.Node): string {
return node.getDisplayName().substr(node.getDisplayNamePrefixLength());
}
export function getFullClassName(node: s.Node): string {
return node.getDisplayName().split(":")[1].split(".").map(util.c2t).join("_");
}
export function getJsType(ctx: CodeGeneratorFileContext, type: s.Type, constructor: boolean): string {
const whichType = type.which();
switch (whichType) {
case s.Type.ANY_POINTER:
return "capnp.Pointer";
case s.Type.BOOL:
return "boolean";
case s.Type.DATA:
return "capnp.Data";
case s.Type.ENUM:
return getFullClassName(lookupNode(ctx, type.getEnum().getTypeId()));
case s.Type.FLOAT32:
case s.Type.FLOAT64:
case s.Type.INT16:
case s.Type.INT32:
case s.Type.INT8:
case s.Type.UINT16:
case s.Type.UINT32:
case s.Type.UINT8:
return "number";
case s.Type.INT64:
return "capnp.Int64";
case s.Type.INTERFACE:
return "capnp.Interface";
case s.Type.LIST:
return `capnp.List${constructor ? "Ctor" : ""}<${getJsType(ctx, type.getList().getElementType(), false)}>`;
case s.Type.STRUCT: {
const c = getFullClassName(lookupNode(ctx, type.getStruct().getTypeId()));
return constructor ? `capnp.StructCtor<${c}>` : c;
}
case s.Type.UINT64:
return "capnp.Uint64";
case s.Type.TEXT:
return "string";
case s.Type.VOID:
return "capnp.Void";
default:
throw new Error(format(E.GEN_UNKNOWN_TYPE, whichType));
}
}
export function getUnnamedUnionFields(node: s.Node): s.Field[] {
if (!node.isStruct()) return [];
return node
.getStruct()
.getFields()
.filter((f) => f.getDiscriminantValue() !== s.Field.NO_DISCRIMINANT);
}
export function hasNode(ctx: CodeGeneratorFileContext, lookup: { getId(): capnp.Uint64 } | capnp.Uint64): boolean {
const id = lookup instanceof capnp.Uint64 ? lookup : lookup.getId();
return ctx.nodes.some((n) => n.getId().equals(id));
}
export function loadRequestedFile(
req: s.CodeGeneratorRequest,
file: s.CodeGeneratorRequest_RequestedFile
): CodeGeneratorFileContext {
trace("compile(%s, %s)", req, file);
const ctx = new CodeGeneratorFileContext(req, file);
const schema = lookupNode(ctx, file.getId());
ctx.tsPath = schema.getDisplayName() + ".ts";
return ctx;
}
export function lookupNode(ctx: CodeGeneratorFileContext, lookup: { getId(): capnp.Uint64 } | capnp.Uint64): s.Node {
const id = lookup instanceof capnp.Uint64 ? lookup : lookup.getId();
const node = ctx.nodes.find((n) => n.getId().equals(id));
if (node === undefined) throw new Error(format(E.GEN_NODE_LOOKUP_FAIL, id));
return node;
}
/**
* Determine whether the given field needs a concrete list class: this is currently the case for composite lists
* (`capnp.CompositeList`) and lists of lists (`capnp.PointerList`).
*
* @param {s.Field} field The field to check.
* @returns {boolean} Returns `true` if the field requires a concrete list class initializer.
*/
export function needsConcreteListClass(field: s.Field): boolean {
if (!field.isSlot()) return false;
const slotType = field.getSlot().getType();
if (!slotType.isList()) return false;
const elementType = slotType.getList().getElementType();
return elementType.isStruct() || elementType.isList();
}