import { setFilter , setSortBy , iterableFirst , setUnion , EqualityMap } from "collection-utils" ; import { defined , panic , assert , assertNever } from "./support/Support" ; import { TypeAttributes , combineTypeAttributes , emptyTypeAttributes , CombinationKind } from "./attributes/TypeAttributes" ; import { Type , PrimitiveType , ArrayType , EnumType , ObjectType , MapType , ClassType , ClassProperty , SetOperationType , UnionType , isPrimitiveStringTypeKind } from "./Type" ; import { stringTypesTypeAttributeKind , StringTypes } from "./attributes/StringTypes" ; export function assertIsObject ( t ) { if ( t instanceof ObjectType ) { return t ; } return panic ( "Supposed object type is not an object type" ) ; } export function assertIsClass ( t ) { if ( ! ( t instanceof ClassType ) ) { return panic ( "Supposed class type is not a class type" ) ; } return t ; } export function setOperationMembersRecursively < T extends SetOperationType > ( setOperation , combinationKind ) ; export function setOperationMembersRecursively < T extends SetOperationType > ( setOperations , combinationKind ) ; export function setOperationMembersRecursively < T extends SetOperationType > ( oneOrMany , combinationKind ) { const setOperations = Array . isArray ( oneOrMany ) ? oneOrMany : [ oneOrMany ] ; const kind = setOperations [ 0 ] . kind ; const includeAny = kind !== "intersection" ; const processedSetOperations = new Set < T > ( ) ; const members = new Set < Type > ( ) ; let attributes = emptyTypeAttributes ; function process ( t ) { if ( t . kind === kind ) { const so = t as T ; if ( processedSetOperations . has ( so ) ) return ; processedSetOperations . add ( so ) ; if ( combinationKind !== undefined ) { attributes = combineTypeAttributes ( combinationKind , attributes , t . getAttributes ( ) ) ; } for ( const m of so . members ) { process ( m ) ; } } else if ( includeAny || t . kind !== "any" ) { members . add ( t ) ; } else { if ( combinationKind !== undefined ) { attributes = combineTypeAttributes ( combinationKind , attributes , t . getAttributes ( ) ) ; } } } for ( const so of setOperations ) { process ( so ) ; } return [ members , attributes ] ; } export function makeGroupsToFlatten < T extends SetOperationType > ( setOperations , include ) { const typeGroups = new EqualityMap < Set < Type > , Set < Type > > ( ) ; for ( const u of setOperations ) { const members = new Set ( setOperationMembersRecursively ( u , undefined ) [ 0 ] ) ; if ( include !== undefined ) { if ( ! include ( members ) ) continue ; } let maybeSet = typeGroups . get ( members ) ; if ( maybeSet === undefined ) { maybeSet = new Set ( ) ; if ( members . size === 1 ) { maybeSet . add ( defined ( iterableFirst ( members ) ) ) ; } } maybeSet . add ( u ) ; typeGroups . set ( members , maybeSet ) ; } return Array . from ( typeGroups . values ( ) ) . map ( ts => Array . from ( ts ) ) ; } export function combineTypeAttributesOfTypes ( combinationKind , types ) { return combineTypeAttributes ( combinationKind , Array . from ( types ) . map ( t => t . getAttributes ( ) ) ) ; } export function isAnyOrNull ( t ) { return t . kind === "any" || t . kind === "null" ; } export function removeNullFromUnion ( t , sortBy = false ) { function sort ( s ) { if ( sortBy === false ) return s ; if ( sortBy === true ) return setSortBy ( s , m => m . kind ) ; return setSortBy ( s , sortBy ) ; } const nullType = t . findMember ( "null" ) ; if ( nullType === undefined ) { return [ null , sort ( t . members ) ] ; } return [ nullType as PrimitiveType , sort ( setFilter ( t . members , m => m . kind !== "null" ) ) ] ; } export function removeNullFromType ( t ) { if ( t . kind === "null" ) { return [ t as PrimitiveType , new Set ( ) ] ; } if ( ! ( t instanceof UnionType ) ) { return [ null , new Set ( [ t ] ) ] ; } return removeNullFromUnion ( t ) ; } export function nullableFromUnion ( t ) { const [ hasNull , nonNulls ] = removeNullFromUnion ( t ) ; if ( hasNull === null ) return null ; if ( nonNulls . size !== 1 ) return null ; return defined ( iterableFirst ( nonNulls ) ) ; } export function nonNullTypeCases ( t ) { return removeNullFromType ( t ) [ 1 ] ; } export function getNullAsOptional ( cp ) { const [ maybeNull , nonNulls ] = removeNullFromType ( cp . type ) ; if ( cp . isOptional ) { return [ true , nonNulls ] ; } return [ maybeNull !== null , nonNulls ] ; } export function isNamedType ( t ) { return [ "class" , "union" , "enum" , "object" ] . indexOf ( t . kind ) >= 0 ; } export type SeparatedNamedTypes = { objects : ReadonlySet < ObjectType > ; enums : ReadonlySet < EnumType > ; unions : ReadonlySet < UnionType > ; } ; export function separateNamedTypes ( types ) { const objects = ( setFilter ( types , t => t . kind === "object" || t . kind === "class" ) as Set < ObjectType > ) as ReadonlySet < ObjectType > ; const enums = ( setFilter ( types , t => t instanceof EnumType ) as Set < EnumType > ) as ReadonlySet < EnumType > ; const unions = ( setFilter ( types , t => t instanceof UnionType ) as Set < UnionType > ) as ReadonlySet < UnionType > ; return { objects , enums , unions } ; } export function directlyReachableTypes < T > ( t , setForType ) { const set = setForType ( t ) ; if ( set !== null ) return set ; return setUnion ( ... Array . from ( t . getNonAttributeChildren ( ) ) . map ( c => directlyReachableTypes ( c , setForType ) ) ) ; } export function directlyReachableSingleNamedType ( type ) { const definedTypes = directlyReachableTypes ( type , t => { if ( ( ! ( t instanceof UnionType ) && isNamedType ( t ) ) || ( t instanceof UnionType && nullableFromUnion ( t ) === null ) ) { return new Set ( [ t ] ) ; } return null ; } ) ; assert ( definedTypes . size <= 1 , "Cannot have more than one defined type per top-level" ) ; return iterableFirst ( definedTypes ) ; } export function stringTypesForType ( t ) { assert ( t . kind === "string" , "Only strings can have string types" ) ; const stringTypes = stringTypesTypeAttributeKind . tryGetInAttributes ( t . getAttributes ( ) ) ; if ( stringTypes === undefined ) { return panic ( "All strings must have a string type attribute" ) ; } return stringTypes ; } export type StringTypeMatchers < U > = { dateType ? : ( dateType : PrimitiveType ) => U ; timeType ? : ( timeType : PrimitiveType ) => U ; dateTimeType ? : ( dateTimeType : PrimitiveType ) => U ; } ; export function matchTypeExhaustive < U > ( t , noneType , anyType , nullType , boolType , integerType , doubleType , stringType , arrayType , classType , mapType , objectType , enumType , unionType , transformedStringType ) { if ( t . isPrimitive ( ) ) { if ( isPrimitiveStringTypeKind ( t . kind ) ) { if ( t . kind === "string" ) { return stringType ( t ) ; } return transformedStringType ( t ) ; } const kind = t . kind ; const f = { none : noneType , any : anyType , null : nullType , bool : boolType , integer : integerType , double : doubleType } [ kind ] ; if ( f !== undefined ) return f ( t ) ; return assertNever ( f ) ; } else if ( t instanceof ArrayType ) return arrayType ( t ) ; else if ( t instanceof ClassType ) return classType ( t ) ; else if ( t instanceof MapType ) return mapType ( t ) ; else if ( t instanceof ObjectType ) return objectType ( t ) ; else if ( t instanceof EnumType ) return enumType ( t ) ; else if ( t instanceof UnionType ) return unionType ( t ) ; return panic ( `Unknown type ${ t . kind }` ) ; } export function matchType < U > ( type , anyType , nullType , boolType , integerType , doubleType , stringType , arrayType , classType , mapType , enumType , unionType , transformedStringType ? ) { function typeNotSupported ( t ) { return panic ( `Unsupported type ${ t . kind } in non-exhaustive match` ) ; } return matchTypeExhaustive ( type , typeNotSupported , anyType , nullType , boolType , integerType , doubleType , stringType , arrayType , classType , mapType , typeNotSupported , enumType , unionType , transformedStringType || typeNotSupported ) ; } export function matchCompoundType ( t , arrayType , classType , mapType , objectType , unionType ) { function ignore < T extends Type > ( _ ) { return ; } return matchTypeExhaustive ( t , ignore , ignore , ignore , ignore , ignore , ignore , ignore , arrayType , classType , mapType , objectType , ignore , unionType , ignore ) ; }