/**
 * 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 Code from '../code' ; import * as Error from '../error' ; import * as FileWriter from '../file-writer' ; import * as Maybe from '../maybe' ; import * as ObjC from '../objc' ; import * as ObjCTypeUtils from '../objc-type-utils' ; import * as ObjectSpec from '../object-spec' ; import * as ObjectSpecCodeUtils from '../object-spec-code-utils' ; interface AttributeDescription { descriptionFunctionImport : ObjC . Import | null ; token : string ; valueGenerator : ( valueAccessor : string ) => string ; } interface ComputedAttributeDescription { attributeFormatString : string ; value : string ; } function useValueAccessor ( valueAccessor ) { return valueAccessor ; } function castValueAccessorValue ( castToApply ) { return function ( valueAccessor ) : string { return '(' + castToApply + ')' + valueAccessor ; } ; } function useFunctionReturnValueAsDescriptionValue ( functionToCall ) { return function ( valueAccessor ) : string { return functionToCall + '(' + valueAccessor + ')' ; } ; } const UI_GEOMETRY_IMPORT = { file : 'UIGeometry.h' , isPublic : false , requiresCPlusPlus : false , library : 'UIKit' } ; const NSOBJECT_ATTRIBUTE_DESCRIPTION = { descriptionFunctionImport : null , token : '%@' , valueGenerator : useValueAccessor } ; function attributeDescriptionForType ( type ) { return ObjCTypeUtils . matchType ( { id : function ( ) { return NSOBJECT_ATTRIBUTE_DESCRIPTION ; } , NSObject : function ( ) { return NSOBJECT_ATTRIBUTE_DESCRIPTION ; } , BOOL : function ( ) { return { descriptionFunctionImport : null , token : '%@' , valueGenerator : function ( valueAccessor ) : string { return valueAccessor + ' ? @"YES" : @"NO"' ; } } ; } , NSInteger : function ( ) { return { descriptionFunctionImport : null , token : '%lld' , valueGenerator : castValueAccessorValue ( 'long long' ) } ; } , NSUInteger : function ( ) { return { descriptionFunctionImport : null , token : '%llu' , valueGenerator : castValueAccessorValue ( 'unsigned long long' ) } ; } , double : function ( ) { return { descriptionFunctionImport : null , token : '%lf' , valueGenerator : useValueAccessor } ; } , float : function ( ) { return { descriptionFunctionImport : null , token : '%f' , valueGenerator : useValueAccessor } ; } , CGFloat : function ( ) { return { descriptionFunctionImport : null , token : '%f' , valueGenerator : useValueAccessor } ; } , NSTimeInterval : function ( ) { return { descriptionFunctionImport : null , token : '%lf' , valueGenerator : useValueAccessor } ; } , uintptr_t : function ( ) { return { descriptionFunctionImport : null , token : '%p' , valueGenerator : castValueAccessorValue ( 'void *' ) } ; } , uint32_t : function ( ) { return { descriptionFunctionImport : null , token : '%u' , valueGenerator : useValueAccessor } ; } , uint64_t : function ( ) { return { descriptionFunctionImport : null , token : '%llu' , valueGenerator : castValueAccessorValue ( 'unsigned long long' ) } ; } , int32_t : function ( ) { return { descriptionFunctionImport : null , token : '%d' , valueGenerator : useValueAccessor } ; } , int64_t : function ( ) { return { descriptionFunctionImport : null , token : '%lld' , valueGenerator : castValueAccessorValue ( 'long long' ) } ; } , SEL : function ( ) { return { descriptionFunctionImport : null , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromSelector' ) } ; } , NSRange : function ( ) { return { descriptionFunctionImport : null , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromRange' ) } ; } , CGRect : function ( ) { return { descriptionFunctionImport : UI_GEOMETRY_IMPORT , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromCGRect' ) } ; } , CGPoint : function ( ) { return { descriptionFunctionImport : UI_GEOMETRY_IMPORT , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromCGPoint' ) } ; } , CGSize : function ( ) { return { descriptionFunctionImport : UI_GEOMETRY_IMPORT , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromCGSize' ) } ; } , UIEdgeInsets : function ( ) { return { descriptionFunctionImport : UI_GEOMETRY_IMPORT , token : '%@' , valueGenerator : useFunctionReturnValueAsDescriptionValue ( 'NSStringFromUIEdgeInsets' ) } ; } , Class : function ( ) { return { descriptionFunctionImport : null , token : '%@' , valueGenerator : useValueAccessor } ; } , dispatch_block_t : function ( ) { return { descriptionFunctionImport : null , token : '%@' , valueGenerator : useValueAccessor } ; } , unmatchedType : function ( ) { return null ! ; } } , type ) ; } function computedAttributeDescriptionFromAttributeDescription ( attributeDescription , name , valueAccessor ) { return { attributeFormatString : name + ': ' + attributeDescription . token + ';' , value : attributeDescription . valueGenerator ( valueAccessor ) } ; } function attributeDescriptionForObjectSpecAttribute ( attribute ) { return attributeDescriptionForType ( ObjectSpecCodeUtils . computeTypeOfAttribute ( attribute ) ) ; } function attributeDescriptionImportMaybeForObjectSpecAttribute ( attribute ) { return attributeDescriptionForType ( ObjectSpecCodeUtils . computeTypeOfAttribute ( attribute ) ) . descriptionFunctionImport ; } function attributeDescriptionImportMaybeForAlgebraicAttribute ( attribute ) { return attributeDescriptionForType ( AlgebraicTypeUtils . computeTypeOfAttribute ( attribute ) ) . descriptionFunctionImport ; } function computedAttributeDescriptionFromAttribute ( attribute ) { const attributeDescription = attributeDescriptionForObjectSpecAttribute ( attribute ) ; return computedAttributeDescriptionFromAttributeDescription ( attributeDescription , attribute . name , ObjectSpecCodeUtils . ivarForAttribute ( attribute ) ) ; } function selectToken ( attributeDescription ) { return attributeDescription . attributeFormatString ; } function selectValue ( attributeDescription ) { return attributeDescription . value ; } function returnStatementForAttributeDescriptions ( attributeDescriptions , name ? ) { const nameString = name ? ' ' + name : '' ; const attributesBeginning = attributeDescriptions . length > 0 ? ' \\n\\t ' : '' ; const attributesFormatString = attributeDescriptions . map ( selectToken ) . join ( ' \\n\\t ' ) ; const attributesValueString = attributeDescriptions . length > 0 ? ', ' + attributeDescriptions . map ( selectValue ) . join ( ', ' ) : '' ; return ( 'return [NSString stringWithFormat:@"%@ -' + nameString + attributesBeginning + attributesFormatString + ' \\n", [super description]' + attributesValueString + '];' ) ; } function descriptionInstanceMethodWithCode ( code ) { return { preprocessors : [ ] , belongsToProtocol : 'NSObject' , code : code , comments : [ ] , compilerAttributes : [ ] , keywords : [ { name : 'description' , argument : null } , ] , returnType : { type : { name : 'NSString' , reference : 'NSString *' } , modifiers : [ ] } } ; } function doesValueAttributeContainAnUnknownType ( attribute ) { const attributeDescription = attributeDescriptionForObjectSpecAttribute ( attribute ) ; return attributeDescription == null ; } function valueAttributeToUnknownTypeError ( objectType , attribute ) { return Maybe . match ( function ( underlyingType ) : Error . Error { return Error . Error ( 'The Description plugin does not know how to format the backing type "' + underlyingType + '" from ' + objectType . typeName + '.' + attribute . name + '. Did you declare the wrong backing type?' ) ; } , function ( ) : Error . Error { return Error . Error ( 'The Description plugin does not know how to format the type "' + attribute . type . name + '" from ' + objectType . typeName + '.' + attribute . name + '. Did you forget to declare a backing type?' ) ; } , attribute . type . underlyingType ) ; } export function createPlugin ( ) { return { additionalFiles : function ( objectType ) : Code . File [ ] { return [ ] ; } , transformBaseFile : function ( objectType , baseFile ) : Code . File { return baseFile ; } , additionalTypes : function ( objectType ) : ObjectSpec . Type [ ] { return [ ] ; } , attributes : function ( objectType ) : ObjectSpec . Attribute [ ] { return [ ] ; } , classMethods : function ( objectType ) : ObjC . Method [ ] { return [ ] ; } , transformFileRequest : function ( request ) : FileWriter . Request { return request ; } , fileType : function ( objectType ) : Code . FileType | null { return null ; } , forwardDeclarations : function ( objectType ) : ObjC . ForwardDeclaration [ ] { return [ ] ; } , functions : function ( objectType ) : ObjC . Function [ ] { return [ ] ; } , headerComments : function ( objectType ) : ObjC . Comment [ ] { return [ ] ; } , implementedProtocols : function ( objectType ) : ObjC . ImplementedProtocol [ ] { return [ ] ; } , imports : function ( objectType ) : ObjC . Import [ ] { const attributeDescriptionImportMaybes = objectType . attributes . map ( attributeDescriptionImportMaybeForObjectSpecAttribute ) ; return Maybe . catMaybes ( attributeDescriptionImportMaybes ) ; } , instanceMethods : function ( objectType ) : ObjC . Method [ ] { if ( objectType . attributes . length > 0 ) { const attributeDescriptions = objectType . attributes . map ( computedAttributeDescriptionFromAttribute ) ; const code = [ returnStatementForAttributeDescriptions ( attributeDescriptions ) , ] ; return [ descriptionInstanceMethodWithCode ( code ) ] ; } else { return [ ] ; } } , macros : function ( valueType ) : ObjC . Macro [ ] { return [ ] ; } , properties : function ( objectType ) : ObjC . Property [ ] { return [ ] ; } , requiredIncludesToRun : [ 'RMDescription' ] , staticConstants : function ( objectType ) : ObjC . Constant [ ] { return [ ] ; } , validationErrors : function ( objectType ) : Error . Error [ ] { return objectType . attributes . filter ( doesValueAttributeContainAnUnknownType ) . map ( ( attribute ) => valueAttributeToUnknownTypeError ( objectType , attribute ) ) ; } , nullability : function ( objectType ) : ObjC . ClassNullability | null { return null ; } , subclassingRestricted : function ( objectType ) : boolean { return false ; } } ; } function computedAttributeDescriptionFromAlgebraicSubtypeAttribute ( subtype , attribute ) { const attributeDescription = attributeDescriptionForType ( AlgebraicTypeUtils . computeTypeOfAttribute ( attribute ) ) ; return computedAttributeDescriptionFromAttributeDescription ( attributeDescription , attribute . name , AlgebraicTypeUtils . valueAccessorForInstanceVariableForAttribute ( subtype , attribute ) ) ; } function returnStatementForAlgebraicSubtype ( algebraicType , subtype ) { return subtype . match ( function ( namedAttributeCollectionSubtype ) { const attributeDescriptions = namedAttributeCollectionSubtype . attributes . map ( ( attribute ) => computedAttributeDescriptionFromAlgebraicSubtypeAttribute ( subtype , attribute ) ) ; return [ returnStatementForAttributeDescriptions ( attributeDescriptions , namedAttributeCollectionSubtype . name ) , ] ; } , function ( attribute ) { const attributeDescriptions = [ computedAttributeDescriptionFromAlgebraicSubtypeAttribute ( subtype , attribute ) , ] ; return [ returnStatementForAttributeDescriptions ( attributeDescriptions ) ] ; } ) ; } function descriptionInstanceMethodCodeForAlgebraicType ( algebraicType ) { return AlgebraicTypeUtils . codeForSwitchingOnSubtypeWithSubtypeMapper ( algebraicType , AlgebraicTypeUtils . valueAccessorForInstanceVariableStoringSubtype ( ) , returnStatementForAlgebraicSubtype ) ; } function doesAlgebraicAttributeContainAnUnknownType ( attribute ) { const attributeDescription = attributeDescriptionForType ( AlgebraicTypeUtils . computeTypeOfAttribute ( attribute ) ) ; return attributeDescription == null ; } function algebraicAttributeToUnknownTypeError ( algebraicType , attribute ) { return Maybe . match ( function ( underlyingType ) : Error . Error { return Error . Error ( 'The Description plugin does not know how to format the backing type "' + underlyingType + '" from ' + algebraicType . name + '.' + attribute . name + '. Did you declare the wrong backing type?' ) ; } , function ( ) : Error . Error { return Error . Error ( 'The Description plugin does not know how to format the type "' + attribute . type . name + '" from ' + algebraicType . name + '.' + attribute . name + '. Did you forget to declare a backing type?' ) ; } , attribute . type . underlyingType ) ; } export function createAlgebraicTypePlugin ( ) { return { additionalFiles : function ( algebraicType ) : Code . File [ ] { return [ ] ; } , transformBaseFile : function ( algebraicType , baseFile ) : Code . File { 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 : function ( algebraicType ) : ObjC . ForwardDeclaration [ ] { return [ ] ; } , functions : function ( algebraicType ) : ObjC . Function [ ] { return [ ] ; } , headerComments : function ( algebraicType ) : ObjC . Comment [ ] { return [ ] ; } , implementedProtocols : function ( algebraicType ) : ObjC . ImplementedProtocol [ ] { return [ ] ; } , imports : function ( algebraicType ) : ObjC . Import [ ] { const attributeDescriptionImportMaybes = AlgebraicTypeUtils . allAttributesFromSubtypes ( algebraicType . subtypes ) . map ( attributeDescriptionImportMaybeForAlgebraicAttribute ) ; return Maybe . catMaybes ( attributeDescriptionImportMaybes ) ; } , instanceMethods : function ( algebraicType ) : ObjC . Method [ ] { const code = descriptionInstanceMethodCodeForAlgebraicType ( algebraicType ) ; return [ descriptionInstanceMethodWithCode ( code ) ] ; } , instanceVariables : function ( algebraicType ) : ObjC . InstanceVariable [ ] { return [ ] ; } , macros : function ( algebraicType ) : ObjC . Macro [ ] { return [ ] ; } , requiredIncludesToRun : [ 'RMDescription' ] , staticConstants : function ( algebraicType ) : ObjC . Constant [ ] { return [ ] ; } , validationErrors : function ( algebraicType ) : Error . Error [ ] { return AlgebraicTypeUtils . allAttributesFromSubtypes ( algebraicType . subtypes ) . filter ( doesAlgebraicAttributeContainAnUnknownType ) . map ( ( attribute ) => algebraicAttributeToUnknownTypeError ( algebraicType , attribute ) ) ; } , nullability : function ( algebraicType ) : ObjC . ClassNullability | null { return null ; } , subclassingRestricted : function ( algebraicType ) : boolean { return false ; } } ; }