/**
 * 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 Code from './code' ; import * as Either from './either' ; import * as Error from './error' ; import * as File from './file' ; import * as FileWriter from './file-writer' ; import * as List from './list' ; import * as Maybe from './maybe' ; import * as ObjC from './objc' ; import * as ObjCCommentUtils from './objc-comment-utils' ; import * as ObjCFileCreation from './objc-file-creation' ; import * as OutputControl from './output-control' ; import * as path from 'path' ; export interface ObjCGenerationPlugIn < T > { additionalFiles : ( typeInformation : T ) => Code . File [ ] ; transformBaseFile : ( typeInformation : T , baseFile : Code . File ) => Code . File ; baseClass : ( typeInformation : T ) => ObjC . BaseClass | null ; blockTypes : ( typeInformation : T ) => ObjC . BlockType [ ] ; classMethods : ( typeInformation : T ) => ObjC . Method [ ] ; comments : ( typeInformation : T ) => ObjC . Comment [ ] ; enumerations : ( typeInformation : T ) => ObjC . Enumeration [ ] ; transformFileRequest : ( writeRequest : FileWriter . Request ) => FileWriter . Request ; fileType : ( typeInformation : T ) => Code . FileType | null ; forwardDeclarations : ( typeInformation : T ) => ObjC . ForwardDeclaration [ ] ; functions : ( typeInformation : T ) => ObjC . Function [ ] ; implementedProtocols : ( typeInformation : T ) => ObjC . ImplementedProtocol [ ] ; imports : ( typeInformation : T ) => ObjC . Import [ ] ; instanceVariables : ( typeInformation : T ) => ObjC . InstanceVariable [ ] ; instanceMethods : ( typeInformation : T ) => ObjC . Method [ ] ; macros : ( typeInformation : T ) => ObjC . Macro [ ] ; properties : ( typeInformation : T ) => ObjC . Property [ ] ; protocols : ( typeInformation : T ) => ObjC . Protocol [ ] ; staticConstants : ( typeInformation : T ) => ObjC . Constant [ ] ; globalVariables : ( typeInformation : T ) => ObjC . GlobalVariable [ ] ; validationErrors : ( typeInformation : T ) => Error . Error [ ] ; nullability : ( typeInformation : T ) => ObjC . ClassNullability | null ; subclassingRestricted : ( typeInformation : T ) => boolean ; structs : ( typeInformation : T ) => Code . Struct [ ] ; requiredIncludesToRun : string [ ] ; } export interface ObjCGenerationRequest < T > { diagnosticIgnores : List . List < string > ; baseClassLibraryName : string | null ; baseClassName : string ; path : File . AbsoluteFilePath ; outputPath : File . AbsoluteFilePath | null ; outputFlags : OutputControl . OutputFlags ; typeInformation : T ; } export interface ObjCGenerationTypeInfoProvider < T > { additionalTypesForType : ( typeInformation : T ) => T [ ] ; typeNameForType : ( typeInformation : T ) => string ; commentsForType : ( typeInformation : T ) => string [ ] ; visibilityForType : ( typeInformation : T ) => ObjC . ClassVisibility | undefined ; } interface ObjCRenderingOptions { renderHeader : boolean ; renderImpl : boolean ; } function buildImports < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . imports ( typeInformation ) ) ; } function buildStaticConstants < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . staticConstants ( typeInformation ) ) ; } function buildglobalVariables < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . globalVariables ( typeInformation ) ) ; } function buildStructs < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . structs ( typeInformation ) ) ; } function buildBlockTypes < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . blockTypes ( typeInformation ) ) ; } function buildClassMethods < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . classMethods ( typeInformation ) ) ; } function buildEnumerations < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . enumerations ( typeInformation ) ) ; } function buildInstanceMethods < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . instanceMethods ( typeInformation ) ) ; } function buildProperties < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . properties ( typeInformation ) ) ; } function buildInstanceVariables < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . instanceVariables ( typeInformation ) ) ; } function buildImplementedProtocols < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . implementedProtocols ( typeInformation ) ) ; } function buildProtocols < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . protocols ( typeInformation ) ) ; } function checkSubclassingRestricted < T > ( typeInformation , soFar , plugin ) { return soFar || plugin . subclassingRestricted ( typeInformation ) ; } function buildFileType < T > ( typeInformation , soFar , plugin ) { return Either . mbind ( function ( maybeExistingType ) { return Maybe . match ( function ( existingType ) { return Maybe . match ( function ( newType ) { if ( newType === existingType ) { return Either . Right < Error . Error , Code . FileType | null > ( newType ) ; } return Either . Left < Error . Error , Code . FileType | null > ( Error . Error ( 'conflicting file type requirements' ) ) ; } , function ( ) { return Either . Right < Error . Error , Code . FileType | null > ( existingType ) ; } , plugin . fileType ( typeInformation ) ) ; } , function ( ) { return Either . Right < Error . Error , Code . FileType | null > ( plugin . fileType ( typeInformation ) ) ; } , maybeExistingType ) ; } , soFar ) ; } function buildNullability < T > ( typeInformation , soFar , plugin ) { return Either . mbind ( function ( maybeExistingType ) { return Maybe . match ( function ( existingType ) { return Maybe . match ( function ( newType ) { if ( newType === existingType ) { return Either . Right < Error . Error , ObjC . ClassNullability | null > ( newType ) ; } throw Either . Left < Error . Error , ObjC . ClassNullability | null > ( Error . Error ( 'conflicting file type requirements' ) ) ; } , function ( ) { return Either . Right < Error . Error , ObjC . ClassNullability | null > ( existingType ) ; } , plugin . nullability ( typeInformation ) ) ; } , function ( ) { return Either . Right < Error . Error , ObjC . ClassNullability | null > ( plugin . nullability ( typeInformation ) ) ; } , maybeExistingType ) ; } , soFar ) ; } function buildForwardDeclarations < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . forwardDeclarations ( typeInformation ) ) ; } function buildFunctions < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . functions ( typeInformation ) ) ; } function buildMacros < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . macros ( typeInformation ) ) ; } function buildComments < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . comments ( typeInformation ) ) ; } function buildAdditionalFiles < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . additionalFiles ( typeInformation ) ) ; } function buildValidationErrors < T > ( typeInformation , soFar , plugin ) { return soFar . concat ( plugin . validationErrors ( typeInformation ) ) ; } function strComparator ( str1 , str2 ) { if ( str1 > str2 ) { return 1 ; } else if ( str1 < str2 ) { return - 1 ; } else { return 0 ; } } function sortInstanceMethodComparitor ( method1 , method2 ) { const significantKeyword1 = method1 . keywords [ 0 ] . name ; const significantKeyword2 = method2 . keywords [ 0 ] . name ; const firstIsInit = significantKeyword1 . indexOf ( 'init' ) !== - 1 ; const secondIsInit = significantKeyword2 . indexOf ( 'init' ) !== - 1 ; if ( firstIsInit ) { if ( secondIsInit ) { return strComparator ( significantKeyword1 , significantKeyword2 ) ; } else { return - 1 ; } } else { if ( secondIsInit ) { return 1 ; } else { return strComparator ( significantKeyword1 , significantKeyword2 ) ; } } } function importListWithBaseImportAppended ( baseClassName , baseClassLibraryName , pluginImports ) { return Maybe . match ( function ( libraryName ) : ObjC . Import [ ] { const importForBaseClass = { file : baseClassName + '.h' , isPublic : true , requiresCPlusPlus : false , library : baseClassLibraryName } ; return [ importForBaseClass ] . concat ( pluginImports ) ; } , function ( ) : ObjC . Import [ ] { return pluginImports ; } , baseClassLibraryName ) ; } function commentListWithPathToValueFile ( pathToValueFile , comments ) { const commentsToUse = comments || [ ] ; return commentsToUse . concat ( ObjCCommentUtils . commentsAsBlockFromStringArray ( [ 'This file is generated using the remodel generation script.' , 'The name of the input file is ' + path . basename ( pathToValueFile . absolutePath ) , ] ) ) ; } function classFileCreationFunctionWithBaseClassAndPlugins < T > ( baseClassName , baseClassLibraryName , diagnosticIgnores , pathToValueFile , plugins , typeProvider ) { return function ( typeInformation , typeName , comments , file ? ) : Either . Either < Error . Error , Code . File > { const fileType = List . foldl < ObjCGenerationPlugIn < T > , Either . Either < Error . Error , Code . FileType | null > > ( ( soFar , plugin ) => buildFileType ( typeInformation , soFar , plugin ) , Either . Right < Error . Error , Code . FileType | null > ( null ) , plugins ) ; const nullabilityEither = List . foldl < ObjCGenerationPlugIn < T > , Either . Either < Error . Error , ObjC . ClassNullability | null > > ( ( soFar , plugin ) => buildNullability ( typeInformation , soFar , plugin ) , Either . Right < Error . Error , ObjC . ClassNullability | null > ( null ) , plugins ) ; const nullability = Either . match ( function ( ) { return ObjC . ClassNullability . default ; } , function ( maybeNullability ) { return Maybe . match ( function ( nullability ) { return nullability ; } , function ( ) { return ObjC . ClassNullability . default ; } , maybeNullability ) ; } , nullabilityEither ) ; const visibility = typeProvider . visibilityForType ( typeInformation ) ; const customPluginBaseClass = List . foldl ( ( currentEither , nextPlugin ) => { return Either . mbind ( ( maybeCurrentBaseClass ) => { const maybeNextBaseClass = nextPlugin . baseClass ( typeInformation ) ; return Maybe . match ( ( [ currentBaseClass , nextBaseClass ] ) => Either . Left ( Error . Error ( `Conflicting base classes ${ currentBaseClass . className } and ${ nextBaseClass . className }` ) ) , ( ) => Either . Right ( Maybe . or ( maybeNextBaseClass , maybeCurrentBaseClass ) ) , Maybe . and ( maybeCurrentBaseClass , maybeNextBaseClass ) ) ; } , currentEither ) ; } , Either . Right ( null as ObjC . BaseClass | null ) , plugins ) ; const baseClass = Either . map ( ( maybeCustom ) => Maybe . match ( ( custom ) => custom , ( ) => ( { className : baseClassName , libraryName : baseClassLibraryName } ) , maybeCustom ) , customPluginBaseClass ) ; return Either . map ( ( [ maybeFileType , baseClass ] ) => { const fileType = Maybe . match ( function ( fileType ) { return fileType ; } , function ( ) { return Code . FileType . ObjectiveC ; } , maybeFileType ) ; const functions = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Function [ ] > ( ( soFar , plugin ) => buildFunctions ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const classes = createClassesForObjectSpecType < T > ( typeInformation , typeName , comments , baseClass . className , functions , plugins , nullability , visibility ) ; const imports = importListWithBaseImportAppended ( baseClass . className , baseClass . libraryName , List . foldl < ObjCGenerationPlugIn < T > , ObjC . Import [ ] > ( ( soFar , plugin ) => buildImports ( typeInformation , soFar , plugin ) , [ ] , plugins ) ) ; const enumerations = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Enumeration [ ] > ( ( soFar , plugin ) => buildEnumerations ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const forwardDeclarations = List . foldl < ObjCGenerationPlugIn < T > , ObjC . ForwardDeclaration [ ] > ( ( soFar , plugin ) => buildForwardDeclarations ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const blockTypes = List . foldl < ObjCGenerationPlugIn < T > , ObjC . BlockType [ ] > ( ( soFar , plugin ) => buildBlockTypes ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const staticConstants = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Constant [ ] > ( ( soFar , plugin ) => buildStaticConstants ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const globalVariables = List . foldl < ObjCGenerationPlugIn < T > , ObjC . GlobalVariable [ ] > ( ( soFar , plugin ) => buildglobalVariables ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const macros = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Macro [ ] > ( ( soFar , plugin ) => buildMacros ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const protocols = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Protocol [ ] > ( ( soFar , plugin ) => buildProtocols ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const structs = List . foldl < ObjCGenerationPlugIn < T > , Code . Struct [ ] > ( ( soFar , plugin ) => buildStructs ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const finalFunctions = classes . length > 0 ? [ ] : functions ; if ( file != null ) { var newFile = file ; newFile . imports = file . imports . concat ( List . foldl < ObjCGenerationPlugIn < T > , ObjC . Import [ ] > ( ( soFar , plugin ) => buildImports ( typeInformation , soFar , plugin ) , [ ] , plugins ) ) ; newFile . enumerations = file . enumerations . concat ( enumerations ) ; newFile . forwardDeclarations = file . forwardDeclarations . concat ( forwardDeclarations ) ; newFile . blockTypes = file . blockTypes . concat ( blockTypes ) ; newFile . diagnosticIgnores = file . diagnosticIgnores . concat ( List . toArray ( diagnosticIgnores ) ) ; newFile . staticConstants = file . staticConstants . concat ( staticConstants ) ; newFile . globalVariables = file . globalVariables . concat ( globalVariables ) ; newFile . functions = file . functions . concat ( finalFunctions ) ; newFile . macros = file . macros . concat ( macros ) ; newFile . classes = file . classes . concat ( classes ) ; newFile . structs = file . structs . concat ( structs ) ; newFile . cppClasses = file . cppClasses ; return newFile ; } else { return { name : typeName , type : fileType , imports : imports , comments : commentListWithPathToValueFile ( pathToValueFile , List . foldl < ObjCGenerationPlugIn < T > , ObjC . Comment [ ] > ( ( soFar , plugin ) => buildComments ( typeInformation , soFar , plugin ) , [ ] , plugins ) ) , enumerations : enumerations , forwardDeclarations : forwardDeclarations , blockTypes : blockTypes , diagnosticIgnores : List . toArray ( diagnosticIgnores ) , staticConstants : staticConstants , globalVariables : globalVariables , functions : finalFunctions , nullability : classes . length > 0 ? undefined : nullability , macros : macros , protocols : protocols , classes : classes , structs : structs , cppClasses : [ ] , namespaces : [ ] } ; } } , Either . and ( fileType , baseClass ) ) ; } ; } function createClassesForObjectSpecType < T > ( typeInformation , typeName , comments , baseClassName , functions , plugins , nullability , visibility ) { const classMethods = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Method [ ] > ( ( soFar , plugin ) => buildClassMethods ( typeInformation , soFar , plugin ) , [ ] , plugins ) . sort ( sortInstanceMethodComparitor ) ; const instanceMethods = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Method [ ] > ( ( soFar , plugin ) => buildInstanceMethods ( typeInformation , soFar , plugin ) , [ ] , plugins ) . sort ( sortInstanceMethodComparitor ) ; const properties = List . foldl < ObjCGenerationPlugIn < T > , ObjC . Property [ ] > ( ( soFar , plugin ) => buildProperties ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const instanceVariables = List . foldl < ObjCGenerationPlugIn < T > , ObjC . InstanceVariable [ ] > ( ( soFar , plugin ) => buildInstanceVariables ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const implementedProtocols = List . foldr < ObjCGenerationPlugIn < T > , ObjC . ImplementedProtocol [ ] > ( ( soFar , plugin ) => buildImplementedProtocols ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; const classDefintionMaybe = createClassIfNecessary ( classMethods , instanceMethods , properties , instanceVariables , functions , implementedProtocols , comments , typeName , baseClassName , nullability , List . foldr < ObjCGenerationPlugIn < T > , boolean > ( ( soFar , plugin ) => checkSubclassingRestricted ( typeInformation , soFar , plugin ) , false , plugins ) , visibility ) ; return Maybe . match ( function ( classInfo ) { return [ classInfo ] ; } , function ( ) { return [ ] ; } , classDefintionMaybe ) ; } function createClassIfNecessary ( classMethods , instanceMethods , properties , instanceVariables , functions , implementedProtocols , comments , typeName , baseClassName , nullability , subclassingRestricted , visibility ) { if ( classMethods . length > 0 || instanceMethods . length > 0 || properties . length > 0 || instanceVariables . length > 0 || implementedProtocols . length > 0 || subclassingRestricted ) { return { baseClassName : baseClassName , covariantTypes : [ ] , comments : ObjCCommentUtils . commentsAsBlockFromStringArray ( comments ) , classMethods : classMethods , functions : functions , inlineBlockTypedefs : [ ] , instanceMethods : instanceMethods , name : typeName , properties : properties , instanceVariables : instanceVariables , implementedProtocols : implementedProtocols , nullability : nullability , subclassingRestricted : subclassingRestricted , visibility : visibility } ; } return null ; } function fileCreationRequestContainingArrayOfPossibleError ( fileCreationRequest ) { return Either . match ( function ( error ) : Either . Either < Error . Error [ ] , FileWriter . FileWriteRequest > { return Either . Left ( [ error ] ) ; } , function ( fileWriteRequest ) : Either . Either < Error . Error [ ] , FileWriter . FileWriteRequest > { return Either . Right ( fileWriteRequest ) ; } , fileCreationRequest ) ; } function fileWriteRequestContainingAdditionalFile ( outputFlags , containingFolderPath , file , fileWriteRequest ) { const fileCreationRequestForAdditionalFile = ObjCFileCreation . fileCreationRequest ( containingFolderPath , file , outputFlags . emitHeaders , outputFlags . emitImplementations ) ; return Either . map ( function ( fileWriteRequestForAdditionalFile ) : FileWriter . FileWriteRequest { const updatedRequest = { name : fileWriteRequest . name , requests : List . append ( fileWriteRequest . requests , fileWriteRequestForAdditionalFile . requests ) } ; return updatedRequest ; } , fileCreationRequestForAdditionalFile ) ; } function fileCreationRequestContainingAdditionalFile ( renderOptions , containingFolderPath , fileCreationRequest , file ) { return Either . mbind ( ( fileWriteRequest ) => { return fileWriteRequestContainingAdditionalFile ( renderOptions , containingFolderPath , file , fileWriteRequest ) ; } , fileCreationRequest ) ; } function transformFileWithPlugins < T > ( baseFile , typeInformation , plugins ) { return List . foldl < ObjCGenerationPlugIn < T > , Code . File > ( ( soFar , plugin ) => plugin . transformBaseFile ( typeInformation , soFar ) , baseFile , plugins ) ; } function buildFileWriteRequest < T > ( request , typeInfoProvider , plugins ) { const classFileFromTypeInfo = classFileCreationFunctionWithBaseClassAndPlugins ( request . baseClassName , request . baseClassLibraryName , request . diagnosticIgnores , request . path , plugins , typeInfoProvider ) ; const outputPath = Maybe . match ( function ( file ) : File . AbsoluteFilePath { return file ; } , function ( ) : File . AbsoluteFilePath { const fullPathAsString = File . getAbsolutePathString ( request . path ) ; return File . getAbsoluteFilePath ( path . dirname ( fullPathAsString ) ) ; } , request . outputPath ) ; const filteredPlugins = List . filter ( function ( p ) { return OutputControl . ShouldEmitPluginFile ( request . outputFlags , p . requiredIncludesToRun [ 0 ] ) ; } , plugins ) ; if ( request . outputFlags . singleFile ) { const baseFileOrError = classFileFromTypeInfo ( request . typeInformation , typeInfoProvider . typeNameForType ( request . typeInformation ) , typeInfoProvider . commentsForType ( request . typeInformation ) ) . map ( function ( file ) { return transformFileWithPlugins ( file , request . typeInformation , filteredPlugins ) ; } ) . mbind ( function ( file ) { const extraTypes = typeInfoProvider . additionalTypesForType ( request . typeInformation ) ; return extraTypes . reduce ( function ( prev , type ) { return prev . mbind ( function ( prevFile ) { return classFileFromTypeInfo ( type , typeInfoProvider . typeNameForType ( type ) , typeInfoProvider . commentsForType ( type ) , prevFile ) . map ( function ( file ) { return transformFileWithPlugins ( file , type , filteredPlugins ) ; } ) . map ( function ( file ) { var newFile = file ; newFile . imports = file . imports . filter ( function ( value ) { return ( value . file != typeInfoProvider . typeNameForType ( type ) + '.h' ) ; } ) ; newFile . forwardDeclarations = file . forwardDeclarations . concat ( [ ObjC . ForwardDeclaration . ForwardClassDeclaration ( typeInfoProvider . typeNameForType ( type ) ) , ] ) ; return newFile ; } ) ; } ) ; } , Either . Right < Error . Error , Code . File > ( file ) ) ; } ) ; const fileWriteRequestOrError = Either . mbind ( function ( file ) { const emptyRequest = Either . Right < Error . Error , FileWriter . FileWriteRequest > ( { name : typeInfoProvider . typeNameForType ( request . typeInformation ) , requests : List . of < FileWriter . Request > ( ) } ) ; const result = fileCreationRequestContainingAdditionalFile ( request . outputFlags , outputPath , emptyRequest , file ) ; return result ; } , baseFileOrError ) ; return fileCreationRequestContainingArrayOfPossibleError ( fileWriteRequestOrError ) ; } else { const typeInfos = [ request . typeInformation ] . concat ( typeInfoProvider . additionalTypesForType ( request . typeInformation ) ) ; const allFileRequests = typeInfos . map ( function ( type ) { const classFile = OutputControl . ShouldEmitObject ( request . outputFlags ) ? classFileFromTypeInfo ( type , typeInfoProvider . typeNameForType ( type ) , typeInfoProvider . commentsForType ( type ) ) . map ( function ( file ) { return [ file ] ; } ) : Either . Right < Error . Error , Code . File [ ] > ( [ ] ) ; const filesToWrite = classFile . map ( function ( files ) { const additionalFiles = List . foldl < ObjCGenerationPlugIn < T > , Code . File [ ] > ( ( soFar , plugin ) => buildAdditionalFiles ( type , soFar , plugin ) , [ ] , filteredPlugins ) ; return files . concat ( additionalFiles ) ; } ) ; const completeFileCreationRequest = Either . mbind ( function ( files ) { return files . reduce ( function ( soFar , currentFile ) { return fileCreationRequestContainingAdditionalFile ( request . outputFlags , outputPath , soFar , currentFile ) ; } , Either . Right < Error . Error , FileWriter . FileWriteRequest > ( { name : typeInfoProvider . typeNameForType ( type ) , requests : List . of < FileWriter . Request > ( ) } ) ) ; } , filesToWrite ) ; return fileCreationRequestContainingArrayOfPossibleError ( completeFileCreationRequest ) ; } ) ; return allFileRequests . reduce ( function ( soFar , current ) { return Either . map ( function ( request ) : FileWriter . FileWriteRequest { return { name : request . name , requests : List . append ( request . requests , current . right ! . requests ) } ; } , soFar ) ; } ) ; } } function valueObjectsToCreateWithPlugins < T > ( plugins , typeInformation ) { const validationErrors = List . foldl < ObjCGenerationPlugIn < T > , Error . Error [ ] > ( ( soFar , plugin ) => buildValidationErrors ( typeInformation , soFar , plugin ) , [ ] , plugins ) ; if ( validationErrors . length > 0 ) { return Either . Left < Error . Error [ ] , T > ( validationErrors ) ; } else { return Either . Right < Error . Error [ ] , T > ( typeInformation ) ; } } export function fileWriteRequest < T > ( request , typeInfoProvider , plugins ) { const validatedRequest = valueObjectsToCreateWithPlugins ( plugins , request . typeInformation ) ; return Either . match ( function ( errors ) { return Either . Left < Error . Error [ ] , FileWriter . FileWriteRequest > ( errors . map ( function ( error ) { return Error . Error ( '[' + request . path . absolutePath + ']' + Error . getReason ( error ) ) ; } ) ) ; } , function ( typeInformation ) { const eitherRequest = buildFileWriteRequest ( request , typeInfoProvider , plugins ) ; return List . foldr ( function ( soFar , plugin ) : Either . Either < Error . Error [ ] , FileWriter . FileWriteRequest > { return Either . map ( function ( request ) : FileWriter . FileWriteRequest { const writeRequest = { name : request . name , requests : List . map ( plugin . transformFileRequest , request . requests ) } ; return writeRequest ; } , soFar ) ; } , eitherRequest , plugins ) ; } , validatedRequest ) ; }