Merge pull request #684 from quicktype/accessor-names
Custom accessor names
This commit is contained in:
Коммит
6f46814ffc
|
@ -0,0 +1,139 @@
|
|||
"use strict";
|
||||
|
||||
import { Map, Set } from "immutable";
|
||||
|
||||
import { TypeAttributeKind, TypeAttributes } from "./TypeAttributes";
|
||||
import { checkStringMap, isStringMap, defined, assert } from "./Support";
|
||||
import { EnumType, ClassType, UnionType, Type } from "./Type";
|
||||
|
||||
export type AccessorEntry = string | { [language: string]: string };
|
||||
|
||||
export type AccessorNames = { [key: string]: AccessorEntry };
|
||||
|
||||
export const accessorNamesTypeAttributeKind = new TypeAttributeKind<AccessorNames>(
|
||||
"accessorNames",
|
||||
undefined,
|
||||
_ => undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
export function isAccessorEntry(x: any): x is AccessorEntry {
|
||||
if (typeof x === "string") {
|
||||
return true;
|
||||
}
|
||||
return isStringMap(x, (v: any): v is string => typeof v === "string");
|
||||
}
|
||||
|
||||
export function checkAccessorNames(x: any): AccessorNames {
|
||||
return checkStringMap(x, isAccessorEntry);
|
||||
}
|
||||
|
||||
// Returns [name, isFixed].
|
||||
function getFromEntry(entry: AccessorEntry, language: string): [string, boolean] | undefined {
|
||||
if (typeof entry === "string") return [entry, false];
|
||||
|
||||
const maybeForLanguage = entry[language];
|
||||
if (maybeForLanguage !== undefined) return [maybeForLanguage, true];
|
||||
|
||||
const maybeWildcard = entry["*"];
|
||||
if (maybeWildcard !== undefined) return [maybeWildcard, false];
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function lookupKey(accessors: AccessorNames, key: string, language: string): [string, boolean] | undefined {
|
||||
if (!Object.prototype.hasOwnProperty.call(accessors, key)) return undefined;
|
||||
|
||||
return getFromEntry(accessors[key], language);
|
||||
}
|
||||
|
||||
export function classPropertyNames(c: ClassType, language: string): Map<string, [string, boolean] | undefined> {
|
||||
const accessors = accessorNamesTypeAttributeKind.tryGetInAttributes(c.getAttributes());
|
||||
const map = c.properties;
|
||||
if (accessors === undefined) return map.map(_ => undefined);
|
||||
return map.map((_cp, n) => lookupKey(accessors, n, language));
|
||||
}
|
||||
|
||||
export function enumCaseNames(e: EnumType, language: string): Map<string, [string, boolean] | undefined> {
|
||||
const accessors = accessorNamesTypeAttributeKind.tryGetInAttributes(e.getAttributes());
|
||||
const map = e.cases.toMap();
|
||||
if (accessors === undefined) return map.map(_ => undefined);
|
||||
return map.map(c => lookupKey(accessors, c, language));
|
||||
}
|
||||
|
||||
export function getAccessorName(
|
||||
names: Map<string, [string, boolean] | undefined>,
|
||||
original: string
|
||||
): [string | undefined, boolean] {
|
||||
const maybeName = names.get(original);
|
||||
if (maybeName === undefined) return [undefined, false];
|
||||
return maybeName;
|
||||
}
|
||||
|
||||
export const unionIdentifierTypeAttributeKind = new TypeAttributeKind<Set<number>>(
|
||||
"unionIdentifier",
|
||||
(a, b) => a.union(b),
|
||||
_ => undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
let nextUnionIdentifier: number = 0;
|
||||
|
||||
export function makeUnionIdentifierAttribute(): TypeAttributes {
|
||||
const attributes = unionIdentifierTypeAttributeKind.makeAttributes(Set([nextUnionIdentifier]));
|
||||
nextUnionIdentifier += 1;
|
||||
return attributes;
|
||||
}
|
||||
|
||||
export const unionMemberNamesTypeAttributeKind = new TypeAttributeKind<Map<number, AccessorEntry>>(
|
||||
"unionMemberNames",
|
||||
(a, b) => a.merge(b),
|
||||
_ => undefined,
|
||||
undefined
|
||||
);
|
||||
|
||||
export function makeUnionMemberNamesAttribute(unionAttributes: TypeAttributes, entry: AccessorEntry): TypeAttributes {
|
||||
const identifiers = defined(unionIdentifierTypeAttributeKind.tryGetInAttributes(unionAttributes));
|
||||
const map = identifiers.toMap().map(_ => entry);
|
||||
return unionMemberNamesTypeAttributeKind.makeAttributes(map);
|
||||
}
|
||||
|
||||
export function unionMemberName(u: UnionType, member: Type, language: string): [string | undefined, boolean] {
|
||||
const identifiers = unionIdentifierTypeAttributeKind.tryGetInAttributes(u.getAttributes());
|
||||
if (identifiers === undefined) return [undefined, false];
|
||||
|
||||
const memberNames = unionMemberNamesTypeAttributeKind.tryGetInAttributes(member.getAttributes());
|
||||
if (memberNames === undefined) return [undefined, false];
|
||||
|
||||
let names: Set<string> = Set();
|
||||
let fixedNames: Set<string> = Set();
|
||||
identifiers.forEach(i => {
|
||||
const maybeEntry = memberNames.get(i);
|
||||
if (maybeEntry === undefined) return;
|
||||
const maybeName = getFromEntry(maybeEntry, language);
|
||||
if (maybeName === undefined) return;
|
||||
const [name, isNameFixed] = maybeName;
|
||||
if (isNameFixed) {
|
||||
fixedNames = fixedNames.add(name);
|
||||
} else {
|
||||
names = names.add(name);
|
||||
}
|
||||
});
|
||||
|
||||
let size: number;
|
||||
let isFixed: boolean;
|
||||
let first = fixedNames.first();
|
||||
if (first !== undefined) {
|
||||
size = fixedNames.size;
|
||||
isFixed = true;
|
||||
} else {
|
||||
first = names.first();
|
||||
if (first === undefined) return [undefined, false];
|
||||
|
||||
size = names.size;
|
||||
isFixed = false;
|
||||
}
|
||||
|
||||
assert(size === 1, `More than one name given for union member: ${names.join(", ")}`);
|
||||
return [first, isFixed];
|
||||
}
|
|
@ -29,13 +29,19 @@ import {
|
|||
descriptionTypeAttributeKind,
|
||||
propertyDescriptionsTypeAttributeKind
|
||||
} from "./TypeAttributes";
|
||||
import { enumCaseNames, classPropertyNames, unionMemberName, getAccessorName } from "./AccessorNames";
|
||||
|
||||
const wordWrap: (s: string) => string = require("wordwrap")(90);
|
||||
|
||||
const givenNameOrder = 1;
|
||||
const inferredNameOrder = 3;
|
||||
|
||||
const classPropertyNameOrder = 2;
|
||||
const assignedClassPropertyNameOrder = 1;
|
||||
|
||||
const enumCaseNameOrder = 2;
|
||||
const assignedEnumCaseNameOrder = 1;
|
||||
|
||||
const unionMemberNameOrder = 4;
|
||||
|
||||
function splitDescription(descriptions: OrderedSet<string> | undefined): string[] | undefined {
|
||||
|
@ -307,8 +313,11 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
c: ClassType,
|
||||
_className: Name,
|
||||
p: ClassProperty,
|
||||
jsonName: string
|
||||
jsonName: string,
|
||||
assignedName: string | undefined
|
||||
): Name | undefined {
|
||||
const namer = this.namerForClassProperty(c, p);
|
||||
if (namer === null) return undefined;
|
||||
// FIXME: This alternative should really depend on what the
|
||||
// actual name of the class ends up being. We can do this
|
||||
// with a DependencyName.
|
||||
|
@ -319,9 +328,9 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
// maybe we'll need global properties for some weird language at
|
||||
// some point.
|
||||
const alternative = `${c.getCombinedName()}_${jsonName}`;
|
||||
const namer = this.namerForClassProperty(c, p);
|
||||
if (namer === null) return undefined;
|
||||
return new SimpleName(OrderedSet([jsonName, alternative]), namer, classPropertyNameOrder);
|
||||
const order = assignedName === undefined ? classPropertyNameOrder : assignedClassPropertyNameOrder;
|
||||
const names = assignedName === undefined ? [jsonName, alternative] : [assignedName];
|
||||
return new SimpleName(OrderedSet(names), namer, order);
|
||||
}
|
||||
|
||||
protected makePropertyDependencyNames(
|
||||
|
@ -342,9 +351,16 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
|
||||
let ns: Namespace | undefined;
|
||||
|
||||
const accessorNames = classPropertyNames(c, this.targetLanguage.name);
|
||||
const names = c.sortedProperties
|
||||
.map((p, jsonName) => {
|
||||
const name = this.makeNameForProperty(c, className, p, jsonName);
|
||||
const [assignedName, isFixed] = getAccessorName(accessorNames, jsonName);
|
||||
let name: Name | undefined;
|
||||
if (isFixed) {
|
||||
name = new FixedName(defined(assignedName));
|
||||
} else {
|
||||
name = this.makeNameForProperty(c, className, p, jsonName, assignedName);
|
||||
}
|
||||
if (name === undefined) return undefined;
|
||||
if (ns === undefined) {
|
||||
ns = new Namespace(c.getCombinedName(), this.globalNamespace, forbiddenNamespaces, forbiddenNames);
|
||||
|
@ -360,9 +376,14 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
};
|
||||
|
||||
protected makeNameForUnionMember(u: UnionType, unionName: Name, t: Type): Name {
|
||||
return new DependencyName(nonNull(this._unionMemberNamer), unionMemberNameOrder, lookup =>
|
||||
this.proposeUnionMemberName(u, unionName, t, lookup)
|
||||
);
|
||||
const [assignedName, isFixed] = unionMemberName(u, t, this.targetLanguage.name);
|
||||
if (isFixed) {
|
||||
return new FixedName(defined(assignedName));
|
||||
}
|
||||
return new DependencyName(nonNull(this._unionMemberNamer), unionMemberNameOrder, lookup => {
|
||||
if (assignedName !== undefined) return assignedName;
|
||||
return this.proposeUnionMemberName(u, unionName, t, lookup);
|
||||
});
|
||||
}
|
||||
|
||||
private addUnionMemberNames = (u: UnionType, unionName: Name): void => {
|
||||
|
@ -388,11 +409,18 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
defined(this._memberNamesStoreView).set(u, names);
|
||||
};
|
||||
|
||||
protected makeNameForEnumCase(e: EnumType, _enumName: Name, caseName: string): Name {
|
||||
protected makeNameForEnumCase(
|
||||
e: EnumType,
|
||||
_enumName: Name,
|
||||
caseName: string,
|
||||
assignedName: string | undefined
|
||||
): Name {
|
||||
// FIXME: See the FIXME in `makeNameForProperty`. We do have global
|
||||
// enum cases, though (in Go), so this is actually useful already.
|
||||
const alternative = `${e.getCombinedName()}_${caseName}`;
|
||||
return new SimpleName(OrderedSet([caseName, alternative]), nonNull(this._enumCaseNamer), enumCaseNameOrder);
|
||||
const order = assignedName === undefined ? enumCaseNameOrder : assignedEnumCaseNameOrder;
|
||||
const names = assignedName === undefined ? [caseName, alternative] : [assignedName];
|
||||
return new SimpleName(OrderedSet(names), nonNull(this._enumCaseNamer), order);
|
||||
}
|
||||
|
||||
// FIXME: this is very similar to addPropertyNameds and addUnionMemberNames
|
||||
|
@ -411,8 +439,16 @@ export abstract class ConvenienceRenderer extends Renderer {
|
|||
ns = new Namespace(e.getCombinedName(), this.globalNamespace, forbiddenNamespaces, forbiddenNames);
|
||||
}
|
||||
let names = Map<string, Name>();
|
||||
const accessorNames = enumCaseNames(e, this.targetLanguage.name);
|
||||
e.cases.forEach(caseName => {
|
||||
names = names.set(caseName, ns.add(this.makeNameForEnumCase(e, enumName, caseName)));
|
||||
const [assignedName, isFixed] = getAccessorName(accessorNames, caseName);
|
||||
let name: Name;
|
||||
if (isFixed) {
|
||||
name = new FixedName(defined(assignedName));
|
||||
} else {
|
||||
name = this.makeNameForEnumCase(e, enumName, caseName, assignedName);
|
||||
}
|
||||
names = names.set(caseName, ns.add(name));
|
||||
});
|
||||
defined(this._caseNamesStoreView).set(e, names);
|
||||
};
|
||||
|
|
|
@ -14,7 +14,8 @@ import {
|
|||
defined,
|
||||
addHashCode,
|
||||
mapSync,
|
||||
forEachSync
|
||||
forEachSync,
|
||||
checkArray
|
||||
} from "./Support";
|
||||
import { TypeGraphBuilder, TypeRef } from "./TypeBuilder";
|
||||
import { TypeNames } from "./TypeNames";
|
||||
|
@ -26,6 +27,13 @@ import {
|
|||
makeTypeAttributesInferred
|
||||
} from "./TypeAttributes";
|
||||
import { JSONSchema, JSONSchemaStore } from "./JSONSchemaStore";
|
||||
import {
|
||||
accessorNamesTypeAttributeKind,
|
||||
checkAccessorNames,
|
||||
makeUnionIdentifierAttribute,
|
||||
isAccessorEntry,
|
||||
makeUnionMemberNamesAttribute
|
||||
} from "./AccessorNames";
|
||||
|
||||
export enum PathElementKind {
|
||||
Root,
|
||||
|
@ -395,6 +403,12 @@ function makeAttributes(schema: StringMap, loc: Location, attributes: TypeAttrib
|
|||
});
|
||||
}
|
||||
|
||||
function makeNonUnionAccessorAttributes(schema: StringMap): TypeAttributes | undefined {
|
||||
const maybeAccessors = schema["qt-accessors"];
|
||||
if (maybeAccessors === undefined) return undefined;
|
||||
return accessorNamesTypeAttributeKind.makeAttributes(checkAccessorNames(maybeAccessors));
|
||||
}
|
||||
|
||||
function checkTypeList(typeOrTypes: any): OrderedSet<string> {
|
||||
if (typeof typeOrTypes === "string") {
|
||||
return OrderedSet([typeOrTypes]);
|
||||
|
@ -591,8 +605,28 @@ export async function addTypesInSchema(
|
|||
}
|
||||
|
||||
async function convertOneOrAnyOf(cases: any, kind: string): Promise<TypeRef> {
|
||||
const maybeAccessors = schema["qt-accessors"];
|
||||
const unionType = typeBuilder.getUniqueUnionType(makeTypeAttributesInferred(typeAttributes), undefined);
|
||||
typeBuilder.setSetOperationMembers(unionType, OrderedSet(await makeTypesFromCases(cases, kind)));
|
||||
const typeRefs = await makeTypesFromCases(cases, kind);
|
||||
|
||||
if (maybeAccessors !== undefined) {
|
||||
const identifierAttribute = makeUnionIdentifierAttribute();
|
||||
typeBuilder.addAttributes(unionType, identifierAttribute);
|
||||
|
||||
const accessors = checkArray(maybeAccessors, isAccessorEntry);
|
||||
assert(
|
||||
typeRefs.length === accessors.length,
|
||||
`Accessor entry array must have the same number of entries as the ${kind}`
|
||||
);
|
||||
for (let i = 0; i < typeRefs.length; i++) {
|
||||
typeBuilder.addAttributes(
|
||||
typeRefs[i],
|
||||
makeUnionMemberNamesAttribute(identifierAttribute, accessors[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
typeBuilder.setSetOperationMembers(unionType, OrderedSet(typeRefs));
|
||||
return unionType;
|
||||
}
|
||||
|
||||
|
@ -610,7 +644,7 @@ export async function addTypesInSchema(
|
|||
predicate = (x: any) => x === null;
|
||||
break;
|
||||
case "integer":
|
||||
predicate = (x: any) => typeof x === "number" && x === Math.floor(x)
|
||||
predicate = (x: any) => typeof x === "number" && x === Math.floor(x);
|
||||
break;
|
||||
default:
|
||||
predicate = (x: any) => typeof x === name;
|
||||
|
@ -652,7 +686,7 @@ export async function addTypesInSchema(
|
|||
unionTypes.push(await makeArrayType());
|
||||
}
|
||||
if (includeObject) {
|
||||
unionTypes.push(...await makeObjectTypes())
|
||||
unionTypes.push(...(await makeObjectTypes()));
|
||||
}
|
||||
|
||||
types.push(typeBuilder.getUniqueUnionType(inferredAttributes, OrderedSet(unionTypes)));
|
||||
|
@ -673,6 +707,11 @@ export async function addTypesInSchema(
|
|||
}
|
||||
if (schema.oneOf !== undefined) {
|
||||
types.push(await convertOneOrAnyOf(schema.oneOf, "oneOf"));
|
||||
} else {
|
||||
const maybeAttributes = makeNonUnionAccessorAttributes(schema);
|
||||
if (maybeAttributes !== undefined) {
|
||||
typeBuilder.addAttributes(intersectionType, maybeAttributes);
|
||||
}
|
||||
}
|
||||
if (schema.anyOf !== undefined) {
|
||||
types.push(await convertOneOrAnyOf(schema.anyOf, "anyOf"));
|
||||
|
|
|
@ -78,6 +78,7 @@ export default class CPlusPlusTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -254,6 +255,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
|
|||
private readonly _caseNamingFunction: Namer;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
|
@ -262,7 +264,7 @@ export class CPlusPlusRenderer extends ConvenienceRenderer {
|
|||
_memberNamingStyle: NamingStyle,
|
||||
_enumeratorNamingStyle: NamingStyle
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
|
||||
this._namespaceNames = List(namespaceName.split("::"));
|
||||
|
||||
|
|
|
@ -75,6 +75,7 @@ export default class CSharpTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -128,6 +129,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
|
|||
protected readonly needAttributes: boolean;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
protected readonly namespaceName: string,
|
||||
|
@ -136,7 +138,7 @@ export class CSharpRenderer extends ConvenienceRenderer {
|
|||
private readonly _useList: boolean,
|
||||
outputFeatures: OutputFeatures
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
this.needHelpers = outputFeatures.helpers;
|
||||
this.needAttributes = outputFeatures.attributes;
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ export default class ElmTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -153,13 +154,14 @@ export class ElmRenderer extends ConvenienceRenderer {
|
|||
private _namedTypeDependents: Map<Name, NamedTypeDependent> = Map();
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
private readonly _moduleName: string,
|
||||
private readonly _useList: boolean
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected forbiddenNamesForGlobalNamespace(): string[] {
|
||||
|
|
|
@ -49,6 +49,7 @@ export default class GoTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -96,12 +97,13 @@ export class GoRenderer extends ConvenienceRenderer {
|
|||
private _topLevelUnmarshalNames = Map<Name, Name>();
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
private readonly _packageName: string
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected topLevelNameStyle(rawName: string): string {
|
||||
|
|
|
@ -31,6 +31,7 @@ export default class JSONSchemaTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
|
|
@ -58,6 +58,7 @@ export class JavaTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -183,12 +184,13 @@ export class JavaRenderer extends ConvenienceRenderer {
|
|||
private _gettersAndSettersForPropertyName: Map<Name, [Name, Name]>;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _packageName: string,
|
||||
private readonly _justTypes: boolean
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
this._gettersAndSettersForPropertyName = Map();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
import { Type, matchType, directlyReachableSingleNamedType, ClassProperty } from "../Type";
|
||||
import { Type, matchType, directlyReachableSingleNamedType, ClassProperty, ClassType } from "../Type";
|
||||
import { TypeGraph } from "../TypeGraph";
|
||||
import {
|
||||
utf16LegalizeCharacters,
|
||||
|
@ -45,6 +45,7 @@ export class JavaScriptTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -96,8 +97,13 @@ function propertyNameStyle(original: string): string {
|
|||
}
|
||||
|
||||
export class JavaScriptRenderer extends ConvenienceRenderer {
|
||||
constructor(graph: TypeGraph, leadingComments: string[] | undefined, private readonly _runtimeTypecheck: boolean) {
|
||||
super(graph, leadingComments);
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _runtimeTypecheck: boolean
|
||||
) {
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected topLevelNameStyle(rawName: string): string {
|
||||
|
@ -124,6 +130,17 @@ export class JavaScriptRenderer extends ConvenienceRenderer {
|
|||
return directlyReachableSingleNamedType(type);
|
||||
}
|
||||
|
||||
protected makeNameForProperty(
|
||||
c: ClassType,
|
||||
className: Name,
|
||||
p: ClassProperty,
|
||||
jsonName: string,
|
||||
_assignedName: string | undefined
|
||||
): Name | undefined {
|
||||
// Ignore the assigned name
|
||||
return super.makeNameForProperty(c, className, p, jsonName, undefined);
|
||||
}
|
||||
|
||||
protected emitDescriptionBlock(lines: string[]): void {
|
||||
this.emitCommentLines(lines, " * ", "/**", " */");
|
||||
}
|
||||
|
|
|
@ -72,6 +72,7 @@ export default class ObjectiveCTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -248,6 +249,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
|
|||
pseudoEnums: Set<EnumType>;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
|
@ -256,7 +258,7 @@ export class ObjectiveCRenderer extends ConvenienceRenderer {
|
|||
private readonly _extraComments: boolean,
|
||||
private readonly _marshalingFunctions: boolean
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
|
||||
// Infer the class prefix from a top-level name if it's not given
|
||||
if (this._classPrefix === DEFAULT_CLASS_PREFIX) {
|
||||
|
|
|
@ -75,6 +75,7 @@ export default class RubyTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -124,12 +125,13 @@ function memberNameStyle(original: string): string {
|
|||
|
||||
export class RubyRenderer extends ConvenienceRenderer {
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
private readonly _strictness: Strictness
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected get commentLineStart(): string {
|
||||
|
|
|
@ -47,7 +47,11 @@ export default class RustTargetLanguage extends TargetLanguage {
|
|||
|
||||
private readonly _deriveDebugOption = new BooleanOption("derive-debug", "Derive Debug impl", false);
|
||||
|
||||
protected get rendererClass(): new (graph: TypeGraph, ...optionValues: any[]) => ConvenienceRenderer {
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
...optionValues: any[]
|
||||
) => ConvenienceRenderer {
|
||||
return RustRenderer;
|
||||
}
|
||||
|
||||
|
@ -182,13 +186,14 @@ const rustStringEscape = utf32ConcatMap(escapeNonPrintableMapper(isPrintable, st
|
|||
|
||||
export class RustRenderer extends ConvenienceRenderer {
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _density: Density,
|
||||
private readonly _visibility: Visibility,
|
||||
private readonly _deriveDebug: boolean
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected makeNamedTypeNamer(): Namer {
|
||||
|
|
|
@ -84,6 +84,7 @@ export default class SwiftTargetLanguage extends TargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -229,6 +230,7 @@ export class SwiftRenderer extends ConvenienceRenderer {
|
|||
private _needNull: boolean = false;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
|
@ -238,7 +240,7 @@ export class SwiftRenderer extends ConvenienceRenderer {
|
|||
private readonly _convenienceInitializers: boolean,
|
||||
private readonly _alamofire: boolean
|
||||
) {
|
||||
super(graph, leadingComments);
|
||||
super(targetLanguage, graph, leadingComments);
|
||||
}
|
||||
|
||||
protected forbiddenNamesForGlobalNamespace(): string[] {
|
||||
|
|
|
@ -10,6 +10,7 @@ import { ConvenienceRenderer } from "../ConvenienceRenderer";
|
|||
import { BooleanOption, Option } from "../RendererOptions";
|
||||
import { JavaScriptTargetLanguage, JavaScriptRenderer } from "./JavaScript";
|
||||
import { defined } from "../Support";
|
||||
import { TargetLanguage } from "../TargetLanguage";
|
||||
|
||||
export abstract class TypeScriptFlowBaseTargetLanguage extends JavaScriptTargetLanguage {
|
||||
private readonly _justTypes = new BooleanOption("just-types", "Interfaces only", false);
|
||||
|
@ -20,6 +21,7 @@ export abstract class TypeScriptFlowBaseTargetLanguage extends JavaScriptTargetL
|
|||
}
|
||||
|
||||
protected abstract get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -32,6 +34,7 @@ export class TypeScriptTargetLanguage extends TypeScriptFlowBaseTargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -44,13 +47,14 @@ export abstract class TypeScriptFlowBaseRenderer extends JavaScriptRenderer {
|
|||
private readonly _inlineUnions: boolean;
|
||||
|
||||
constructor(
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
private readonly _justTypes: boolean,
|
||||
declareUnions: boolean,
|
||||
runtimeTypecheck: boolean
|
||||
) {
|
||||
super(graph, leadingComments, runtimeTypecheck);
|
||||
super(targetLanguage, graph, leadingComments, runtimeTypecheck);
|
||||
this._inlineUnions = !declareUnions;
|
||||
}
|
||||
|
||||
|
@ -214,6 +218,7 @@ export class FlowTargetLanguage extends TypeScriptFlowBaseTargetLanguage {
|
|||
}
|
||||
|
||||
protected get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
|
|
@ -8,6 +8,7 @@ import { Name, Namespace, assignNames } from "./Naming";
|
|||
import { Source, Sourcelike, NewlineSource, annotated, sourcelikeToSource, newline } from "./Source";
|
||||
import { AnnotationData, IssueAnnotationData } from "./Annotation";
|
||||
import { assert, panic, StringMap } from "./Support";
|
||||
import { TargetLanguage } from "./TargetLanguage";
|
||||
|
||||
export type RenderResult = {
|
||||
sources: OrderedMap<string, Source>;
|
||||
|
@ -46,7 +47,7 @@ export abstract class Renderer {
|
|||
// @ts-ignore: Initialized in startEmit, which is called from the constructor
|
||||
private _preventBlankLine: boolean;
|
||||
|
||||
constructor(protected readonly typeGraph: TypeGraph, protected readonly leadingComments: string[] | undefined) {
|
||||
constructor(protected readonly targetLanguage: TargetLanguage, protected readonly typeGraph: TypeGraph, protected readonly leadingComments: string[] | undefined) {
|
||||
this._finishedFiles = Map();
|
||||
this.startEmit();
|
||||
}
|
||||
|
|
|
@ -17,17 +17,43 @@ export function setUnion<T, TSet extends Set<T>>(a: TSet, b: TSet): TSet {
|
|||
|
||||
export type StringMap = { [name: string]: any };
|
||||
|
||||
export function checkStringMap(x: any): StringMap {
|
||||
if (typeof x !== "object") {
|
||||
return panic(`Value must be an object, but is ${x}`);
|
||||
export function isStringMap(x: any): x is StringMap;
|
||||
export function isStringMap<T>(x: any, checkValue: (v: any) => v is T): x is { [name: string]: T };
|
||||
export function isStringMap<T>(x: any, checkValue?: (v: any) => v is T): boolean {
|
||||
if (typeof x !== "object" || Array.isArray(x) || x === null) {
|
||||
return false;
|
||||
}
|
||||
return x;
|
||||
if (checkValue !== undefined) {
|
||||
for (const k of Object.getOwnPropertyNames(x)) {
|
||||
const v = x[k];
|
||||
if (!checkValue(v)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
export function checkArray(x: any): any[] {
|
||||
export function checkStringMap(x: any): StringMap;
|
||||
export function checkStringMap<T>(x: any, checkValue: (v: any) => v is T): { [name: string]: T };
|
||||
export function checkStringMap<T>(x: any, checkValue?: (v: any) => v is T): StringMap {
|
||||
if (isStringMap(x, checkValue as any)) return x;
|
||||
return panic(`Value must be an object, but is ${x}`);
|
||||
}
|
||||
|
||||
export function checkArray(x: any): any[];
|
||||
export function checkArray<T>(x: any, checkItem: (v: any) => v is T): T[];
|
||||
export function checkArray<T>(x: any, checkItem?: (v: any) => v is T): T[] {
|
||||
if (!Array.isArray(x)) {
|
||||
return panic(`Value must be an array, but is ${x}`);
|
||||
}
|
||||
if (checkItem !== undefined) {
|
||||
for (const v of x) {
|
||||
if (!checkItem(v)) {
|
||||
return panic(`Array item does not satisfy constraint: ${v}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,12 @@ export abstract class TargetLanguage {
|
|||
return { actual, display };
|
||||
}
|
||||
|
||||
get name(): string {
|
||||
return defined(this.names[0]);
|
||||
}
|
||||
|
||||
protected abstract get rendererClass(): new (
|
||||
targetLanguage: TargetLanguage,
|
||||
graph: TypeGraph,
|
||||
leadingComments: string[] | undefined,
|
||||
...optionValues: any[]
|
||||
|
@ -41,6 +46,7 @@ export abstract class TargetLanguage {
|
|||
rendererOptions: { [name: string]: any }
|
||||
): Renderer {
|
||||
return new this.rendererClass(
|
||||
this,
|
||||
graph,
|
||||
leadingComments,
|
||||
...this.getOptions().map(o => o.getValue(rendererOptions))
|
||||
|
|
|
@ -5,10 +5,10 @@ import { Map, OrderedSet, hash } from "immutable";
|
|||
import { panic, setUnion } from "./Support";
|
||||
export class TypeAttributeKind<T> {
|
||||
public readonly combine: (a: T, b: T) => T;
|
||||
public readonly makeInferred: (a: T) => T;
|
||||
public readonly makeInferred: (a: T) => (T | undefined);
|
||||
public readonly stringify: (a: T) => string | undefined;
|
||||
|
||||
constructor(readonly name: string, combine: ((a: T, b: T) => T) | undefined, makeInferred: ((a: T) => T) | undefined, stringify: ((a: T) => string | undefined) | undefined) {
|
||||
constructor(readonly name: string, combine: ((a: T, b: T) => T) | undefined, makeInferred: ((a: T) => (T | undefined)) | undefined, stringify: ((a: T) => string | undefined) | undefined) {
|
||||
if (combine === undefined) {
|
||||
combine = () => {
|
||||
return panic(`Cannot combine type attribute ${name}`);
|
||||
|
@ -93,7 +93,7 @@ export function combineTypeAttributes(firstOrArray: TypeAttributes[] | TypeAttri
|
|||
}
|
||||
|
||||
export function makeTypeAttributesInferred(attr: TypeAttributes): TypeAttributes {
|
||||
return attr.map((value, kind) => kind.makeInferred(value));
|
||||
return attr.map((value, kind) => kind.makeInferred(value)).filter(v => v !== undefined);
|
||||
}
|
||||
|
||||
export const descriptionTypeAttributeKind = new TypeAttributeKind<OrderedSet<string>>("description", setUnion, a => a, undefined);
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"union": 123,
|
||||
"enum": "red",
|
||||
"foo": "FOO!",
|
||||
"bar": "BAAAAR"
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"union": {
|
||||
"oneOf": [
|
||||
{
|
||||
"type": "boolean"
|
||||
},
|
||||
{
|
||||
"type": "number"
|
||||
}
|
||||
],
|
||||
"qt-accessors": ["presence", "amount"]
|
||||
},
|
||||
"enum": {
|
||||
"type": "string",
|
||||
"enum": ["red", "green", "blue"],
|
||||
"qt-accessors": {
|
||||
"red": "fire",
|
||||
"green": "grass"
|
||||
}
|
||||
},
|
||||
"foo": {
|
||||
"type": "string"
|
||||
},
|
||||
"bar": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"qt-accessors": {
|
||||
"union": "unionization",
|
||||
"enum": "enumerification",
|
||||
"foo": {
|
||||
"cs": "Fu_uu",
|
||||
"java": "Goo"
|
||||
},
|
||||
"bar": {
|
||||
"go": "Bah",
|
||||
"*": "barre"
|
||||
}
|
||||
},
|
||||
"required": ["union", "enum", "foo", "bar"],
|
||||
"additionalProperties": false
|
||||
}
|
Загрузка…
Ссылка в новой задаче