import { iterableFirst , mapFromIterable , mapMap , mapFromObject , setUnionManyInto , mapMergeInto } from "collection-utils" ; import { TypeAttributeKind , TypeAttributes } from "./TypeAttributes" ; import { defined , isStringMap , checkStringMap , checkArray } from "../support/Support" ; import { EnumType , UnionType , Type , ObjectType } from "../Type" ; import { messageAssert } from "../Messages" ; import { JSONSchema } from "../input/JSONSchemaStore" ; import { Ref , JSONSchemaType , JSONSchemaAttributes } from "../input/JSONSchemaInput" ; export type AccessorEntry = string | Map < string , string > ; export type AccessorNames = Map < string , AccessorEntry > ; class AccessorNamesTypeAttributeKind extends TypeAttributeKind < AccessorNames > { constructor ( ) { super ( "accessorNames" ) ; } makeInferred ( _ ) { return undefined ; } } export const accessorNamesTypeAttributeKind = new AccessorNamesTypeAttributeKind ( ) ; function getFromEntry ( entry , language ) { if ( typeof entry === "string" ) return [ entry , false ] ; const maybeForLanguage = entry . get ( language ) ; if ( maybeForLanguage !== undefined ) return [ maybeForLanguage , true ] ; const maybeWildcard = entry . get ( "*" ) ; if ( maybeWildcard !== undefined ) return [ maybeWildcard , false ] ; return undefined ; } export function lookupKey ( accessors , key , language ) { const entry = accessors . get ( key ) ; if ( entry === undefined ) return undefined ; return getFromEntry ( entry , language ) ; } export function objectPropertyNames ( o , language ) { const accessors = accessorNamesTypeAttributeKind . tryGetInAttributes ( o . getAttributes ( ) ) ; const map = o . getProperties ( ) ; if ( accessors === undefined ) return mapMap ( map , _ => undefined ) ; return mapMap ( map , ( _cp , n ) => lookupKey ( accessors , n , language ) ) ; } export function enumCaseNames ( e , language ) { const accessors = accessorNamesTypeAttributeKind . tryGetInAttributes ( e . getAttributes ( ) ) ; if ( accessors === undefined ) return mapMap ( e . cases . entries ( ) , _ => undefined ) ; return mapMap ( e . cases . entries ( ) , c => lookupKey ( accessors , c , language ) ) ; } export function getAccessorName ( names , original ) { const maybeName = names . get ( original ) ; if ( maybeName === undefined ) return [ undefined , false ] ; return maybeName ; } class UnionIdentifierTypeAttributeKind extends TypeAttributeKind < ReadonlySet < number > > { constructor ( ) { super ( "unionIdentifier" ) ; } combine ( arr ) { return setUnionManyInto ( new Set ( ) , arr ) ; } makeInferred ( _ ) { return new Set ( ) ; } } export const unionIdentifierTypeAttributeKind = new UnionIdentifierTypeAttributeKind ( ) ; let nextUnionIdentifier = 0 ; export function makeUnionIdentifierAttribute ( ) { const attributes = unionIdentifierTypeAttributeKind . makeAttributes ( new Set ( [ nextUnionIdentifier ] ) ) ; nextUnionIdentifier += 1 ; return attributes ; } class UnionMemberNamesTypeAttributeKind extends TypeAttributeKind < Map < number , AccessorEntry > > { constructor ( ) { super ( "unionMemberNames" ) ; } combine ( arr ) { const result = new Map < number , AccessorEntry > ( ) ; for ( const m of arr ) { mapMergeInto ( result , m ) ; } return result ; } } export const unionMemberNamesTypeAttributeKind = new UnionMemberNamesTypeAttributeKind ( ) ; export function makeUnionMemberNamesAttribute ( unionAttributes , entry ) { const identifiers = defined ( unionIdentifierTypeAttributeKind . tryGetInAttributes ( unionAttributes ) ) ; const map = mapFromIterable ( identifiers , _ => entry ) ; return unionMemberNamesTypeAttributeKind . makeAttributes ( map ) ; } export function unionMemberName ( u , member , language ) { const identifiers = unionIdentifierTypeAttributeKind . tryGetInAttributes ( u . getAttributes ( ) ) ; if ( identifiers === undefined ) return [ undefined , false ] ; const memberNames = unionMemberNamesTypeAttributeKind . tryGetInAttributes ( member . getAttributes ( ) ) ; if ( memberNames === undefined ) return [ undefined , false ] ; const names = new Set < string > ( ) ; const fixedNames = new Set < string > ( ) ; for ( const i of identifiers ) { const maybeEntry = memberNames . get ( i ) ; if ( maybeEntry === undefined ) continue ; const maybeName = getFromEntry ( maybeEntry , language ) ; if ( maybeName === undefined ) continue ; const [ name , isNameFixed ] = maybeName ; if ( isNameFixed ) { fixedNames . add ( name ) ; } else { names . add ( name ) ; } } let size ; let isFixed ; let first = iterableFirst ( fixedNames ) ; if ( first !== undefined ) { size = fixedNames . size ; isFixed = true ; } else { first = iterableFirst ( names ) ; if ( first === undefined ) return [ undefined , false ] ; size = names . size ; isFixed = false ; } messageAssert ( size === 1 , "SchemaMoreThanOneUnionMemberName" , { names : Array . from ( names ) } ) ; return [ first , isFixed ] ; } function isAccessorEntry ( x ) { if ( typeof x === "string" ) { return true ; } return isStringMap ( x , ( v ) : v is string => typeof v === "string" ) ; } function makeAccessorEntry ( ae ) { if ( typeof ae === "string" ) return ae ; return mapFromObject ( ae ) ; } export function makeAccessorNames ( x ) { const stringMap = checkStringMap ( x , isAccessorEntry ) ; return mapMap ( mapFromObject ( stringMap ) , makeAccessorEntry ) ; } export function accessorNamesAttributeProducer ( schema , canonicalRef , _types , cases ) { if ( typeof schema !== "object" ) return undefined ; const maybeAccessors = schema [ "qt-accessors" ] ; if ( maybeAccessors === undefined ) return undefined ; if ( cases === undefined ) { return { forType : accessorNamesTypeAttributeKind . makeAttributes ( makeAccessorNames ( maybeAccessors ) ) } ; } else { const identifierAttribute = makeUnionIdentifierAttribute ( ) ; const accessors = checkArray ( maybeAccessors , isAccessorEntry ) ; messageAssert ( cases . length === accessors . length , "SchemaWrongAccessorEntryArrayLength" , { operation : "oneOf" , ref : canonicalRef . push ( "oneOf" ) } ) ; const caseAttributes = accessors . map ( accessor => makeUnionMemberNamesAttribute ( identifierAttribute , makeAccessorEntry ( accessor ) ) ) ; return { forUnion : identifierAttribute , forCases : caseAttributes } ; } }