/**
 * Copyright (c) 2016-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */ import * as AlgebraicType from '../algebraic-type' ; import * as AlgebraicTypeUtils from '../algebraic-type-utils' ; import * as AlgebraicTypeUtilsForMatching from '../algebraic-type-utils-for-matching' ; import * as Code from '../code' ; import * as Error from '../error' ; import * as FileWriter from '../file-writer' ; import * as ObjC from '../objc' ; import * as StringUtils from '../string-utils' ; function matchingBlockTypeForPlugin ( assumeNonnull ) { return { name : 'ObjectType' , underlyingType : 'ObjectType' , modifiers : assumeNonnull ? [ ObjC . KeywordArgumentModifier . Nullable ( ) ] : [ ] , defaultValue : 'nil' } ; } function fileNameForAlgebraicType ( algebraicType ) { return algebraicType . name + 'Matcher' ; } function parameterNameForAlgebraicType ( algebraicType ) { return StringUtils . lowercased ( StringUtils . stringRemovingCapitalizedPrefix ( algebraicType . name ) ) ; } function conditionallyAddToSpread < T > ( addIt , value ) { return addIt ? [ value ] : [ ] ; } function genericMatcherImportsForAlgebraicType ( algebraicType , forBaseFile ) { return [ { file : 'Foundation.h' , isPublic : true , requiresCPlusPlus : false , library : 'Foundation' } , { file : algebraicType . name + '.h' , isPublic : true , requiresCPlusPlus : false , library : algebraicType . libraryName } , ... conditionallyAddToSpread ( ! forBaseFile , { file : fileNameForAlgebraicType ( algebraicType ) + '.h' , isPublic : false , requiresCPlusPlus : false , library : null } ) , ] ; } function blockInvocationWrapper ( blockInvocation ) { return 'result = ' + blockInvocation + ';' ; } function buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , soFar , subtype ) { return soFar . concat ( AlgebraicTypeUtilsForMatching . buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , subtype , blockInvocationWrapper ) ) ; } function genericMatchingCodeForAlgebraicType ( algebraicType ) { const localVariableDeclaration = [ '__block id result = nil;' , '' ] ; const matchingBlockCode = algebraicType . subtypes . reduce ( ( soFar , subtype ) => buildLocalFunctionBlockDefinitionsForSubtype ( algebraicType , soFar , subtype ) , [ ] ) ; const keywordPartsForMatchInvocation = algebraicType . subtypes . reduce ( ( soFar , subtype , idx ) => AlgebraicTypeUtilsForMatching . buildKeywordPartsForInvokingMatchMethodForSubtype ( algebraicType , soFar , subtype , idx ) , [ ] ) ; const matchInvocation = [ '[' + parameterNameForAlgebraicType ( algebraicType ) + ' ' + keywordPartsForMatchInvocation . join ( ' ' ) + '];' , ] ; const returnStatement = [ '' , 'return result;' ] ; return localVariableDeclaration . concat ( matchingBlockCode ) . concat ( matchInvocation ) . concat ( returnStatement ) ; } function firstKeywordForGenericMatchMethod ( algebraicType ) { return { name : 'match' , argument : { name : parameterNameForAlgebraicType ( algebraicType ) , modifiers : [ ] , type : { name : algebraicType . name , reference : algebraicType . name + '*' } } } ; } function keywordsForGenericAlgebraicTypeMatcher ( algebraicType , matchingBlockType ) { const firstKeyword = firstKeywordForGenericMatchMethod ( algebraicType ) ; const subtypeKeywords = algebraicType . subtypes . map ( ( subtype ) => AlgebraicTypeUtils . keywordForMatchMethodFromSubtype ( algebraicType , matchingBlockType , false , subtype ) ) ; return [ firstKeyword ] . concat ( subtypeKeywords ) ; } function classMethodForGenericMatchingOfAlgebraicType ( algebraicType ) { const matchingBlockType = matchingBlockTypeForPlugin ( algebraicType . includes . indexOf ( 'RMAssumeNonnull' ) > - 1 ) ; return { preprocessors : [ ] , belongsToProtocol : null , code : genericMatchingCodeForAlgebraicType ( algebraicType ) , comments : [ ] , compilerAttributes : [ ] , keywords : keywordsForGenericAlgebraicTypeMatcher ( algebraicType , matchingBlockType ) , returnType : AlgebraicTypeUtils . returnTypeForMatchingBlockType ( matchingBlockType ) } ; } function blockTypesForAlgebraicType ( algebraicType ) { const matchingBlockType = matchingBlockTypeForPlugin ( algebraicType . includes . indexOf ( 'RMAssumeNonnull' ) > - 1 ) ; return algebraicType . subtypes . map ( ( subtype ) => AlgebraicTypeUtils . blockTypeForSubtype ( algebraicType , matchingBlockType , subtype ) ) ; } function genericMatchingClassForAlgebraicType ( algebraicType ) { return { baseClassName : 'NSObject' , covariantTypes : [ 'ObjectType' ] , classMethods : [ classMethodForGenericMatchingOfAlgebraicType ( algebraicType ) ] , comments : [ ] , inlineBlockTypedefs : blockTypesForAlgebraicType ( algebraicType ) , instanceMethods : [ ] , name : fileNameForAlgebraicType ( algebraicType ) , properties : [ ] , instanceVariables : [ ] , implementedProtocols : [ ] , nullability : algebraicType . includes . indexOf ( 'RMAssumeNonnull' ) > - 1 ? ObjC . ClassNullability . assumeNonnull : ObjC . ClassNullability . default , subclassingRestricted : true } ; } function genericMatchingFileForAlgebraicType ( algebraicType ) { return { name : fileNameForAlgebraicType ( algebraicType ) , type : Code . FileType . ObjectiveC , imports : genericMatcherImportsForAlgebraicType ( algebraicType , false ) , forwardDeclarations : AlgebraicTypeUtilsForMatching . forwardDeclarationsForAlgebraicType ( algebraicType ) , comments : [ ] , enumerations : [ ] , blockTypes : [ ] , staticConstants : [ ] , globalVariables : [ ] , functions : [ ] , classes : [ genericMatchingClassForAlgebraicType ( algebraicType ) ] , diagnosticIgnores : [ ] , structs : [ ] , cppClasses : [ ] , namespaces : [ ] , macros : [ ] } ; } export function createAlgebraicTypePlugin ( ) { return { additionalFiles : function ( algebraicType ) : Code . File [ ] { return [ genericMatchingFileForAlgebraicType ( algebraicType ) ] ; } , transformBaseFile : function ( algebraicType , baseFile ) : Code . File { baseFile . imports = baseFile . imports . concat ( genericMatcherImportsForAlgebraicType ( algebraicType , true ) ) ; baseFile . classes . push ( genericMatchingClassForAlgebraicType ( algebraicType ) ) ; return baseFile ; } , blockTypes : function ( algebraicType ) : ObjC . BlockType [ ] { return [ ] ; } , classMethods : function ( algebraicType ) : ObjC . Method [ ] { return [ ] ; } , enumerations : function ( algebraicType ) : ObjC . Enumeration [ ] { return [ ] ; } , transformFileRequest : function ( request ) : FileWriter . Request { return request ; } , fileType : function ( algebraicType ) : Code . FileType | null { return null ; } , forwardDeclarations : AlgebraicTypeUtilsForMatching . forwardDeclarationsForAlgebraicType , functions : function ( algebraicType ) : ObjC . Function [ ] { return [ ] ; } , headerComments : function ( algebraicType ) : ObjC . Comment [ ] { return [ ] ; } , implementedProtocols : function ( algebraicType ) : ObjC . ImplementedProtocol [ ] { return [ ] ; } , imports : function ( algebraicType ) : ObjC . Import [ ] { return [ ] ; } , instanceMethods : function ( algebraicType ) : ObjC . Method [ ] { return [ ] ; } , instanceVariables : function ( algebraicType ) : ObjC . InstanceVariable [ ] { return [ ] ; } , macros : function ( algebraicType ) : ObjC . Macro [ ] { return [ ] ; } , requiredIncludesToRun : [ 'GenericMatching' ] , staticConstants : function ( algebraicType ) : ObjC . Constant [ ] { return [ ] ; } , validationErrors : function ( algebraicType ) : Error . Error [ ] { return [ ] ; } , nullability : function ( algebraicType ) : ObjC . ClassNullability | null { return null ; } , subclassingRestricted : function ( algebraicType ) : boolean { return false ; } } ; }