Put string enum cases in a type attribute
This commit is contained in:
Родитель
9387370253
Коммит
94887379cf
|
@ -157,7 +157,7 @@ export function combineClasses(
|
|||
clique,
|
||||
attributes,
|
||||
builder,
|
||||
unionBuilderForUnification(builder, false, false, false, conflateNumbers),
|
||||
unionBuilderForUnification(builder, false, false, conflateNumbers),
|
||||
conflateNumbers,
|
||||
forwardingRef
|
||||
);
|
||||
|
|
|
@ -22,7 +22,7 @@ export function flattenUnions(
|
|||
let needsRepeat = false;
|
||||
|
||||
function replace(types: Set<Type>, builder: GraphRewriteBuilder<Type>, forwardingRef: TypeRef): TypeRef {
|
||||
const unionBuilder = new UnifyUnionBuilder(builder, true, makeObjectTypes, true, trefs => {
|
||||
const unionBuilder = new UnifyUnionBuilder(builder, makeObjectTypes, true, trefs => {
|
||||
assert(trefs.length > 0, "Must have at least one type to build union");
|
||||
trefs = trefs.map(tref => builder.reconstituteType(tref.deref()[0]));
|
||||
if (trefs.length === 1) {
|
||||
|
|
|
@ -113,7 +113,7 @@ function makeScalar(builder: TypeBuilder, ft: GQLType): TypeRef {
|
|||
return builder.getPrimitiveType("double");
|
||||
default:
|
||||
// FIXME: support ID specifically?
|
||||
return builder.getStringType(emptyTypeAttributes, undefined);
|
||||
return builder.getStringType(emptyTypeAttributes, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -427,7 +427,7 @@ export function makeGraphQLQueryTypes(
|
|||
namesTypeAttributeKind.makeAttributes(
|
||||
TypeNames.make(OrderedSet(["error"]), OrderedSet(["graphQLError"]), false)
|
||||
),
|
||||
OrderedMap({ message: new ClassProperty(builder.getStringType(emptyTypeAttributes, undefined), false) })
|
||||
OrderedMap({ message: new ClassProperty(builder.getStringType(emptyTypeAttributes, null), false) })
|
||||
);
|
||||
const errorArray = builder.getArrayType(errorType);
|
||||
builder.addAttributes(
|
||||
|
|
|
@ -79,11 +79,7 @@ export class TypeReconstituter<TBuilder extends BaseGraphRewriteBuilder> {
|
|||
}
|
||||
|
||||
getPrimitiveType(kind: PrimitiveTypeKind): void {
|
||||
this.registerAndAddAttributes(this.builderForNewType().getPrimitiveType(kind, this._forwardingRef));
|
||||
}
|
||||
|
||||
getStringType(enumCases: OrderedMap<string, number> | undefined): void {
|
||||
this.register(this.builderForNewType().getStringType(this._typeAttributes, enumCases, this._forwardingRef));
|
||||
this.register(this.builderForNewType().getPrimitiveType(kind, this._typeAttributes, this._forwardingRef));
|
||||
}
|
||||
|
||||
getEnumType(cases: OrderedSet<string>): void {
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
import { Set, OrderedMap, OrderedSet } from "immutable";
|
||||
|
||||
import { Type, StringType, UnionType } from "./Type";
|
||||
import { combineTypeAttributesOfTypes } from "./TypeUtils";
|
||||
import { Type, PrimitiveType, UnionType, stringEnumCasesTypeAttributeKind } from "./Type";
|
||||
import { combineTypeAttributesOfTypes, stringEnumCases } from "./TypeUtils";
|
||||
import { TypeGraph } from "./TypeGraph";
|
||||
import { TypeRef, StringTypeMapping } from "./TypeBuilder";
|
||||
import { GraphRewriteBuilder } from "./GraphRewriting";
|
||||
|
@ -12,32 +12,32 @@ import { combineTypeAttributes } from "./TypeAttributes";
|
|||
|
||||
const MIN_LENGTH_FOR_ENUM = 10;
|
||||
|
||||
function shouldBeEnum(t: StringType): OrderedMap<string, number> | undefined {
|
||||
const enumCases = t.enumCases;
|
||||
function shouldBeEnum(t: PrimitiveType): OrderedMap<string, number> | undefined {
|
||||
const enumCases = stringEnumCases(t);
|
||||
if (enumCases !== undefined) {
|
||||
assert(enumCases.size > 0, "How did we end up with zero enum cases?");
|
||||
const someCaseIsNotNumber = enumCases.keySeq().some(key => /^(\-|\+)?[0-9]+(\.[0-9]+)?$/.test(key) === false);
|
||||
const numValues = enumCases.map(n => n).reduce<number>((a, b) => a + b);
|
||||
if (numValues >= MIN_LENGTH_FOR_ENUM && enumCases.size < Math.sqrt(numValues) && someCaseIsNotNumber) {
|
||||
return t.enumCases;
|
||||
return enumCases;
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function replaceString(
|
||||
group: Set<StringType>,
|
||||
builder: GraphRewriteBuilder<StringType>,
|
||||
group: Set<PrimitiveType>,
|
||||
builder: GraphRewriteBuilder<PrimitiveType>,
|
||||
forwardingRef: TypeRef
|
||||
): TypeRef {
|
||||
assert(group.size === 1);
|
||||
const t = defined(group.first());
|
||||
const attributes = t.getAttributes();
|
||||
const attributes = t.getAttributes().filterNot((_, k) => k === stringEnumCasesTypeAttributeKind);
|
||||
const maybeEnumCases = shouldBeEnum(t);
|
||||
if (maybeEnumCases !== undefined) {
|
||||
return builder.getEnumType(attributes, maybeEnumCases.keySeq().toOrderedSet(), forwardingRef);
|
||||
}
|
||||
return builder.getStringType(attributes, undefined, forwardingRef);
|
||||
return builder.getStringType(attributes, null, forwardingRef);
|
||||
}
|
||||
|
||||
// A union needs replacing if it contains more than one string type, one of them being
|
||||
|
@ -78,9 +78,9 @@ export function inferEnums(
|
|||
): TypeGraph {
|
||||
const allStrings = graph
|
||||
.allTypesUnordered()
|
||||
.filter(t => t instanceof StringType)
|
||||
.filter(t => t.kind === "string")
|
||||
.map(t => [t])
|
||||
.toArray() as StringType[][];
|
||||
.toArray() as PrimitiveType[][];
|
||||
return graph.rewrite("infer enums", stringTypeMapping, false, allStrings, debugPrintReconstitution, replaceString);
|
||||
}
|
||||
|
||||
|
|
|
@ -127,7 +127,7 @@ export function inferMaps(
|
|||
shouldBe,
|
||||
c.getAttributes(),
|
||||
builder,
|
||||
unionBuilderForUnification(builder, false, false, false, conflateNumbers),
|
||||
unionBuilderForUnification(builder, false, false, conflateNumbers),
|
||||
conflateNumbers
|
||||
),
|
||||
forwardingRef
|
||||
|
|
|
@ -616,10 +616,10 @@ export async function addTypesInSchema(
|
|||
default:
|
||||
// FIXME: Output a warning here instead to indicate that
|
||||
// the format is uninterpreted.
|
||||
return typeBuilder.getStringType(inferredAttributes, undefined);
|
||||
return typeBuilder.getStringType(inferredAttributes, null);
|
||||
}
|
||||
}
|
||||
return typeBuilder.getStringType(inferredAttributes, undefined);
|
||||
return typeBuilder.getStringType(inferredAttributes, null);
|
||||
}
|
||||
|
||||
async function makeArrayType(): Promise<TypeRef> {
|
||||
|
|
|
@ -14,7 +14,6 @@ import {
|
|||
UnionType,
|
||||
PrimitiveStringTypeKind,
|
||||
PrimitiveTypeKind,
|
||||
StringType,
|
||||
ArrayType,
|
||||
isPrimitiveStringTypeKind,
|
||||
isPrimitiveTypeKind,
|
||||
|
@ -96,7 +95,7 @@ class IntersectionAccumulator
|
|||
|
||||
// If the unrestricted string type is part of the union, this doesn't add
|
||||
// any more restrictions.
|
||||
if (members.find(t => t instanceof StringType) === undefined) {
|
||||
if (members.find(t => t.kind === "string") === undefined) {
|
||||
this._primitiveStringTypes = this._primitiveStringTypes.intersect(kinds);
|
||||
}
|
||||
}
|
||||
|
@ -127,7 +126,7 @@ class IntersectionAccumulator
|
|||
const enums = members.filter(t => t instanceof EnumType) as OrderedSet<EnumType>;
|
||||
const attributes = combineTypeAttributesOfTypes(enums);
|
||||
this._enumAttributes = combineTypeAttributes(this._enumAttributes, attributes);
|
||||
if (members.find(t => t instanceof StringType) !== undefined) {
|
||||
if (members.find(t => t.kind === "string") !== undefined) {
|
||||
return;
|
||||
}
|
||||
const newCases = OrderedSet<string>().union(...enums.map(t => t.cases).toArray());
|
||||
|
@ -358,15 +357,6 @@ class IntersectionUnionBuilder extends UnionBuilder<
|
|||
return this._createdNewIntersections;
|
||||
}
|
||||
|
||||
protected makeEnum(
|
||||
cases: string[],
|
||||
_counts: { [name: string]: number },
|
||||
typeAttributes: TypeAttributes,
|
||||
forwardingRef: TypeRef | undefined
|
||||
): TypeRef {
|
||||
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(cases), forwardingRef);
|
||||
}
|
||||
|
||||
protected makeObject(
|
||||
maybeData: [PropertyMap, OrderedSet<Type> | undefined] | undefined,
|
||||
typeAttributes: TypeAttributes,
|
||||
|
@ -411,8 +401,7 @@ export function resolveIntersections(
|
|||
const intersections = types.filter(t => t instanceof IntersectionType) as Set<IntersectionType>;
|
||||
const [members, intersectionAttributes] = setOperationMembersRecursively(intersections.toArray());
|
||||
if (members.isEmpty()) {
|
||||
const t = builder.getPrimitiveType("any", forwardingRef);
|
||||
builder.addAttributes(t, intersectionAttributes);
|
||||
const t = builder.getPrimitiveType("any", intersectionAttributes, forwardingRef);
|
||||
return t;
|
||||
}
|
||||
if (members.size === 1) {
|
||||
|
|
60
src/Type.ts
60
src/Type.ts
|
@ -6,7 +6,7 @@ import { defined, panic, assert, mapOptional } from "./Support";
|
|||
import { TypeRef } from "./TypeBuilder";
|
||||
import { TypeReconstituter, BaseGraphRewriteBuilder } from "./GraphRewriting";
|
||||
import { TypeNames, namesTypeAttributeKind } from "./TypeNames";
|
||||
import { TypeAttributes } from "./TypeAttributes";
|
||||
import { TypeAttributes, TypeAttributeKind } from "./TypeAttributes";
|
||||
import { ErrorMessage, messageAssert } from "./Messages";
|
||||
|
||||
export type PrimitiveStringTypeKind = "string" | "date" | "time" | "date-time";
|
||||
|
@ -44,6 +44,19 @@ function orderedSetUnion<T>(sets: OrderedSet<OrderedSet<T>>): OrderedSet<T> {
|
|||
return setArray[0].union(...setArray.slice(1));
|
||||
}
|
||||
|
||||
export const stringEnumCasesTypeAttributeKind = new TypeAttributeKind<OrderedMap<string, number> | null>(
|
||||
"stringEnumCases",
|
||||
true,
|
||||
(a, b) => {
|
||||
if (a === null || b === null) {
|
||||
return null;
|
||||
}
|
||||
return a.mergeWith((x, y) => x + y, b);
|
||||
},
|
||||
_ => undefined,
|
||||
m => (m === null ? "no enum" : `${m.size.toString()} enums: ${m.keySeq().first()}, ...`)
|
||||
);
|
||||
|
||||
export abstract class Type {
|
||||
constructor(readonly typeRef: TypeRef, readonly kind: TypeKind) {}
|
||||
|
||||
|
@ -198,13 +211,6 @@ export class PrimitiveType extends Type {
|
|||
// @ts-ignore: This is initialized in the Type constructor
|
||||
readonly kind: PrimitiveTypeKind;
|
||||
|
||||
constructor(typeRef: TypeRef, kind: PrimitiveTypeKind, checkKind: boolean = true) {
|
||||
if (checkKind) {
|
||||
assert(kind !== "string", "Cannot instantiate a PrimitiveType as string");
|
||||
}
|
||||
super(typeRef, kind);
|
||||
}
|
||||
|
||||
get children(): OrderedSet<Type> {
|
||||
return OrderedSet();
|
||||
}
|
||||
|
@ -234,44 +240,6 @@ export class PrimitiveType extends Type {
|
|||
}
|
||||
}
|
||||
|
||||
export function stringTypeIdentity(
|
||||
attributes: TypeAttributes,
|
||||
enumCases: OrderedMap<string, number> | undefined
|
||||
): List<any> | undefined {
|
||||
if (enumCases !== undefined) return undefined;
|
||||
// mapOptional(ec => ec.keySeq().toSet(), enumCases)
|
||||
return List(["string", identityAttributes(attributes)]);
|
||||
}
|
||||
|
||||
export class StringType extends PrimitiveType {
|
||||
constructor(typeRef: TypeRef, readonly enumCases: OrderedMap<string, number> | undefined) {
|
||||
super(typeRef, "string", false);
|
||||
}
|
||||
|
||||
get identity(): List<any> | undefined {
|
||||
return stringTypeIdentity(this.getAttributes(), this.enumCases);
|
||||
}
|
||||
|
||||
reconstitute<T extends BaseGraphRewriteBuilder>(builder: TypeReconstituter<T>): void {
|
||||
builder.getStringType(this.enumCases);
|
||||
}
|
||||
|
||||
protected structuralEqualityStep(
|
||||
_other: Type,
|
||||
_conflateNumbers: boolean,
|
||||
_queue: (a: Type, b: Type) => boolean
|
||||
): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
get debugPrintKind(): string {
|
||||
if (this.enumCases === undefined) {
|
||||
return "string";
|
||||
}
|
||||
return `string (${this.enumCases.size} enums)`;
|
||||
}
|
||||
}
|
||||
|
||||
export function arrayTypeIdentity(attributes: TypeAttributes, itemsRef: TypeRef): List<any> {
|
||||
return List(["array", identityAttributes(attributes), itemsRef]);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
import { Map, OrderedMap, OrderedSet, Set, List } from "immutable";
|
||||
import { Map, OrderedMap, OrderedSet, Set, List, is } from "immutable";
|
||||
|
||||
import {
|
||||
PrimitiveTypeKind,
|
||||
|
@ -12,18 +12,17 @@ import {
|
|||
ClassType,
|
||||
UnionType,
|
||||
PrimitiveStringTypeKind,
|
||||
StringType,
|
||||
ClassProperty,
|
||||
IntersectionType,
|
||||
ObjectType,
|
||||
stringTypeIdentity,
|
||||
primitiveTypeIdentity,
|
||||
enumTypeIdentity,
|
||||
mapTypeIdentify,
|
||||
arrayTypeIdentity,
|
||||
classTypeIdentity,
|
||||
unionTypeIdentity,
|
||||
intersectionTypeIdentity
|
||||
intersectionTypeIdentity,
|
||||
stringEnumCasesTypeAttributeKind
|
||||
} from "./Type";
|
||||
import { removeNullFromUnion } from "./TypeUtils";
|
||||
import { TypeGraph } from "./TypeGraph";
|
||||
|
@ -141,7 +140,8 @@ export class TypeBuilder {
|
|||
}
|
||||
const tref = forwardingRef !== undefined ? forwardingRef : this.reserveTypeRef();
|
||||
if (attributes !== undefined) {
|
||||
this.addAttributes(tref, attributes);
|
||||
const index = tref.index;
|
||||
this.typeAttributes[index] = combineTypeAttributes(this.typeAttributes[index], attributes);
|
||||
}
|
||||
const t = creator(tref);
|
||||
this.commitType(tref, t);
|
||||
|
@ -158,9 +158,19 @@ export class TypeBuilder {
|
|||
}
|
||||
|
||||
addAttributes(tref: TypeRef, attributes: TypeAttributes): void {
|
||||
assert(attributes.every((_, k) => !k.inIdentity), "Can't add identity type attributes to an existing type");
|
||||
const index = tref.index;
|
||||
this.typeAttributes[index] = combineTypeAttributes(this.typeAttributes[index], attributes);
|
||||
const existingAttributes = this.typeAttributes[index];
|
||||
assert(
|
||||
attributes.every((v, k) => {
|
||||
if (!k.inIdentity) return true;
|
||||
const existing = existingAttributes.get(k);
|
||||
if (existing === undefined) return false;
|
||||
return is(existing, v);
|
||||
}),
|
||||
"Can't add different identity type attributes to an existing type"
|
||||
);
|
||||
const nonIdentityAttributes = attributes.filterNot((_, k) => k.inIdentity);
|
||||
this.typeAttributes[index] = combineTypeAttributes(existingAttributes, nonIdentityAttributes);
|
||||
}
|
||||
|
||||
makeNullable(tref: TypeRef, attributes: TypeAttributes): TypeRef {
|
||||
|
@ -223,7 +233,11 @@ export class TypeBuilder {
|
|||
if (maybeTypeRef !== undefined) {
|
||||
const result = this.forwardIfNecessary(forwardingRef, maybeTypeRef);
|
||||
if (attributes !== undefined) {
|
||||
this.addAttributes(result, attributes);
|
||||
// We only add the attributes that are not in the identity, since
|
||||
// we found the type based on its identity, i.e. all the identity
|
||||
// attributes must be in there already, and we have a check that
|
||||
// asserts that no identity attributes are added later.
|
||||
this.addAttributes(result, attributes.filter((_, k) => !k.inIdentity));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -237,30 +251,44 @@ export class TypeBuilder {
|
|||
this.registerTypeForIdentity(t.identity, t.typeRef);
|
||||
}
|
||||
|
||||
getPrimitiveType(kind: PrimitiveTypeKind, forwardingRef?: TypeRef): TypeRef {
|
||||
assert(kind !== "string", "Use getStringType to create strings");
|
||||
getPrimitiveType(kind: PrimitiveTypeKind, attributes?: TypeAttributes, forwardingRef?: TypeRef): TypeRef {
|
||||
if (attributes === undefined) {
|
||||
attributes = emptyTypeAttributes;
|
||||
}
|
||||
let enumCases = kind === "string" ? undefined : null;
|
||||
if (kind === "date") kind = this._stringTypeMapping.date;
|
||||
if (kind === "time") kind = this._stringTypeMapping.time;
|
||||
if (kind === "date-time") kind = this._stringTypeMapping.dateTime;
|
||||
if (kind === "string") {
|
||||
return this.getStringType(emptyTypeAttributes, undefined, forwardingRef);
|
||||
return this.getStringType(attributes, enumCases, forwardingRef);
|
||||
}
|
||||
return this.getOrAddType(
|
||||
primitiveTypeIdentity(kind, emptyTypeAttributes),
|
||||
tr => new PrimitiveType(tr, kind),
|
||||
undefined,
|
||||
attributes,
|
||||
forwardingRef
|
||||
);
|
||||
}
|
||||
|
||||
getStringType(
|
||||
attributes: TypeAttributes,
|
||||
cases: OrderedMap<string, number> | undefined,
|
||||
cases: OrderedMap<string, number> | null | undefined,
|
||||
forwardingRef?: TypeRef
|
||||
): TypeRef {
|
||||
const existingEnumAttribute = attributes.find((_, k) => k === stringEnumCasesTypeAttributeKind);
|
||||
assert(
|
||||
(cases === undefined) !== (existingEnumAttribute === undefined),
|
||||
"Must instantiate string type with one enum case attribute"
|
||||
);
|
||||
if (existingEnumAttribute === undefined) {
|
||||
attributes = combineTypeAttributes(
|
||||
attributes,
|
||||
stringEnumCasesTypeAttributeKind.makeAttributes(defined(cases))
|
||||
);
|
||||
}
|
||||
return this.getOrAddType(
|
||||
stringTypeIdentity(attributes, cases),
|
||||
tr => new StringType(tr, cases),
|
||||
primitiveTypeIdentity("string", attributes),
|
||||
tr => new PrimitiveType(tr, "string"),
|
||||
attributes,
|
||||
forwardingRef
|
||||
);
|
||||
|
|
|
@ -393,9 +393,8 @@ export function noneToAny(
|
|||
[noneTypes.toArray()],
|
||||
debugPrintReconstitution,
|
||||
(types, builder, forwardingRef) => {
|
||||
const tref = builder.getPrimitiveType("any", forwardingRef);
|
||||
const attributes = combineTypeAttributesOfTypes(types);
|
||||
builder.addAttributes(tref, attributes);
|
||||
const tref = builder.getPrimitiveType("any", attributes, forwardingRef);
|
||||
return tref;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
import { OrderedSet, Collection, Map, Set } from "immutable";
|
||||
import { OrderedSet, Collection, Map, Set, OrderedMap } from "immutable";
|
||||
|
||||
import { defined, panic, assert, assertNever } from "./Support";
|
||||
import { TypeAttributes, combineTypeAttributes, emptyTypeAttributes } from "./TypeAttributes";
|
||||
import {
|
||||
Type,
|
||||
PrimitiveType,
|
||||
StringType,
|
||||
ArrayType,
|
||||
EnumType,
|
||||
ObjectType,
|
||||
|
@ -15,7 +14,8 @@ import {
|
|||
ClassType,
|
||||
ClassProperty,
|
||||
SetOperationType,
|
||||
UnionType
|
||||
UnionType,
|
||||
stringEnumCasesTypeAttributeKind
|
||||
} from "./Type";
|
||||
|
||||
export function assertIsObject(t: Type): ObjectType {
|
||||
|
@ -191,6 +191,18 @@ export function directlyReachableSingleNamedType(type: Type): Type | undefined {
|
|||
return definedTypes.first();
|
||||
}
|
||||
|
||||
export function stringEnumCases(t: PrimitiveType): OrderedMap<string, number> | undefined {
|
||||
assert(t.kind === "string", "Only strings can be considered enums");
|
||||
const enumCases = stringEnumCasesTypeAttributeKind.tryGetInAttributes(t.getAttributes());
|
||||
if (enumCases === undefined) {
|
||||
return panic("All strings must have an enum case attribute");
|
||||
}
|
||||
if (enumCases === null) {
|
||||
return undefined;
|
||||
}
|
||||
return enumCases;
|
||||
}
|
||||
|
||||
export type StringTypeMatchers<U> = {
|
||||
dateType?: (dateType: PrimitiveType) => U;
|
||||
timeType?: (timeType: PrimitiveType) => U;
|
||||
|
@ -205,7 +217,7 @@ export function matchTypeExhaustive<U>(
|
|||
boolType: (boolType: PrimitiveType) => U,
|
||||
integerType: (integerType: PrimitiveType) => U,
|
||||
doubleType: (doubleType: PrimitiveType) => U,
|
||||
stringType: (stringType: StringType) => U,
|
||||
stringType: (stringType: PrimitiveType) => U,
|
||||
arrayType: (arrayType: ArrayType) => U,
|
||||
classType: (classType: ClassType) => U,
|
||||
mapType: (mapType: MapType) => U,
|
||||
|
@ -247,7 +259,7 @@ export function matchType<U>(
|
|||
boolType: (boolType: PrimitiveType) => U,
|
||||
integerType: (integerType: PrimitiveType) => U,
|
||||
doubleType: (doubleType: PrimitiveType) => U,
|
||||
stringType: (stringType: StringType) => U,
|
||||
stringType: (stringType: PrimitiveType) => U,
|
||||
arrayType: (arrayType: ArrayType) => U,
|
||||
classType: (classType: ClassType) => U,
|
||||
mapType: (mapType: MapType) => U,
|
||||
|
|
|
@ -90,7 +90,6 @@ function countProperties(
|
|||
export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp, TypeRef[], TypeRef[]> {
|
||||
constructor(
|
||||
typeBuilder: TypeBuilder & TypeLookerUp,
|
||||
private readonly _makeEnums: boolean,
|
||||
private readonly _makeObjectTypes: boolean,
|
||||
private readonly _makeClassesFixed: boolean,
|
||||
private readonly _unifyTypes: (typesToUnify: TypeRef[]) => TypeRef
|
||||
|
@ -98,19 +97,6 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
|
|||
super(typeBuilder);
|
||||
}
|
||||
|
||||
protected makeEnum(
|
||||
enumCases: string[],
|
||||
counts: { [name: string]: number },
|
||||
typeAttributes: TypeAttributes,
|
||||
forwardingRef: TypeRef | undefined
|
||||
): TypeRef {
|
||||
if (this._makeEnums) {
|
||||
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(enumCases), forwardingRef);
|
||||
} else {
|
||||
return this.typeBuilder.getStringType(typeAttributes, OrderedMap(counts), forwardingRef);
|
||||
}
|
||||
}
|
||||
|
||||
protected makeObject(
|
||||
objectRefs: TypeRef[],
|
||||
typeAttributes: TypeAttributes,
|
||||
|
@ -149,7 +135,7 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
|
|||
return tref;
|
||||
} else {
|
||||
const [properties, additionalProperties, lostTypeAttributes] = getCliqueProperties(objectTypes, types => {
|
||||
assert(types.size > 0, "Property has no type");
|
||||
assert(types.size > 0, "Property has no type");
|
||||
return this._unifyTypes(types.map(t => t.typeRef).toArray());
|
||||
});
|
||||
if (lostTypeAttributes) {
|
||||
|
@ -187,17 +173,16 @@ export class UnifyUnionBuilder extends UnionBuilder<TypeBuilder & TypeLookerUp,
|
|||
|
||||
export function unionBuilderForUnification<T extends Type>(
|
||||
typeBuilder: GraphRewriteBuilder<T>,
|
||||
makeEnums: boolean,
|
||||
makeObjectTypes: boolean,
|
||||
makeClassesFixed: boolean,
|
||||
conflateNumbers: boolean
|
||||
): UnionBuilder<TypeBuilder & TypeLookerUp, TypeRef[], TypeRef[]> {
|
||||
return new UnifyUnionBuilder(typeBuilder, makeEnums, makeObjectTypes, makeClassesFixed, trefs =>
|
||||
return new UnifyUnionBuilder(typeBuilder, makeObjectTypes, makeClassesFixed, trefs =>
|
||||
unifyTypes(
|
||||
Set(trefs.map(tref => tref.deref()[0])),
|
||||
emptyTypeAttributes,
|
||||
typeBuilder,
|
||||
unionBuilderForUnification(typeBuilder, makeEnums, makeObjectTypes, makeClassesFixed, conflateNumbers),
|
||||
unionBuilderForUnification(typeBuilder, makeObjectTypes, makeClassesFixed, conflateNumbers),
|
||||
conflateNumbers
|
||||
)
|
||||
);
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { Map, Set, OrderedMap, OrderedSet } from "immutable";
|
||||
|
||||
import { TypeKind, PrimitiveStringTypeKind, Type, UnionType } from "./Type";
|
||||
import { TypeKind, PrimitiveStringTypeKind, Type, UnionType, stringEnumCasesTypeAttributeKind } from "./Type";
|
||||
import { matchTypeExhaustive } from "./TypeUtils";
|
||||
import {
|
||||
TypeAttributes,
|
||||
|
@ -131,7 +131,8 @@ export class UnionAccumulator<TArray, TObject> implements UnionTypeProvider<TArr
|
|||
|
||||
addEnumCases(cases: OrderedMap<string, number>, attributes: TypeAttributes): void {
|
||||
if (this.have("string")) {
|
||||
this.addStringType("string", attributes);
|
||||
const enumAttributes = stringEnumCasesTypeAttributeKind.makeAttributes(cases);
|
||||
this.addStringType("string", combineTypeAttributes(attributes, enumAttributes));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -241,14 +242,7 @@ export class TypeRefUnionAccumulator extends UnionAccumulator<TypeRef, TypeRef>
|
|||
_boolType => this.addBool(attributes),
|
||||
_integerType => this.addInteger(attributes),
|
||||
_doubleType => this.addDouble(attributes),
|
||||
stringType => {
|
||||
const enumCases = stringType.enumCases;
|
||||
if (enumCases === undefined) {
|
||||
this.addStringType("string", attributes);
|
||||
} else {
|
||||
this.addEnumCases(enumCases, attributes);
|
||||
}
|
||||
},
|
||||
_stringType => this.addStringType("string", attributes),
|
||||
arrayType => this.addArray(arrayType.items.typeRef, attributes),
|
||||
classType => this.addObject(classType.typeRef, attributes),
|
||||
mapType => this.addObject(mapType.typeRef, attributes),
|
||||
|
@ -276,12 +270,15 @@ export class TypeRefUnionAccumulator extends UnionAccumulator<TypeRef, TypeRef>
|
|||
export abstract class UnionBuilder<TBuilder extends TypeBuilder, TArrayData, TObjectData> {
|
||||
constructor(protected readonly typeBuilder: TBuilder) {}
|
||||
|
||||
protected abstract makeEnum(
|
||||
protected makeEnum(
|
||||
cases: string[],
|
||||
counts: { [name: string]: number },
|
||||
_counts: { [name: string]: number },
|
||||
typeAttributes: TypeAttributes,
|
||||
forwardingRef: TypeRef | undefined
|
||||
): TypeRef;
|
||||
): TypeRef {
|
||||
return this.typeBuilder.getEnumType(typeAttributes, OrderedSet(cases), forwardingRef);
|
||||
}
|
||||
|
||||
protected abstract makeObject(
|
||||
objects: TObjectData,
|
||||
typeAttributes: TypeAttributes,
|
||||
|
@ -309,11 +306,15 @@ export abstract class UnionBuilder<TBuilder extends TypeBuilder, TArrayData, TOb
|
|||
case "date":
|
||||
case "time":
|
||||
case "date-time":
|
||||
const t = this.typeBuilder.getPrimitiveType(kind, forwardingRef);
|
||||
this.typeBuilder.addAttributes(t, typeAttributes);
|
||||
return t;
|
||||
return this.typeBuilder.getPrimitiveType(kind, typeAttributes, forwardingRef);
|
||||
case "string":
|
||||
return this.typeBuilder.getStringType(typeAttributes, undefined, forwardingRef);
|
||||
return this.typeBuilder.getStringType(
|
||||
typeAttributes,
|
||||
typeAttributes.findKey((_, k) => k === stringEnumCasesTypeAttributeKind) !== undefined
|
||||
? undefined
|
||||
: null,
|
||||
forwardingRef
|
||||
);
|
||||
case "enum":
|
||||
return this.makeEnum(typeProvider.enumCases, typeProvider.enumCaseMap, typeAttributes, forwardingRef);
|
||||
case "object":
|
||||
|
|
|
@ -211,6 +211,7 @@ export const GoLanguage: Language = {
|
|||
"github-events.json",
|
||||
"reddit.json",
|
||||
"nbl-stats.json",
|
||||
"recursive.json",
|
||||
"0cffa.json",
|
||||
"0e0c2.json",
|
||||
"127a1.json",
|
||||
|
|
Загрузка…
Ссылка в новой задаче