hashGenerator: generatorForInvokingSubGenerators()

in src/plugins/equality.ts [711:885]


          hashGenerator: generatorForInvokingSubGenerators([
            generatorForProvidingHashValuesFromGeneratorOfTypeWithSubValue(
              CGFLOAT_TYPE,
              'top',
            ),
            generatorForProvidingHashValuesFromGeneratorOfTypeWithSubValue(
              CGFLOAT_TYPE,
              'left',
            ),
            generatorForProvidingHashValuesFromGeneratorOfTypeWithSubValue(
              CGFLOAT_TYPE,
              'bottom',
            ),
            generatorForProvidingHashValuesFromGeneratorOfTypeWithSubValue(
              CGFLOAT_TYPE,
              'right',
            ),
          ]),
        };
      },
      Class: function () {
        return NSOBJECT_GENERATION_GROUP;
      },
      dispatch_block_t: function () {
        return NSOBJECT_GENERATION_GROUP;
      },
      unmatchedType: function () {
        return null!;
      },
    },
    type,
  );
}

function generatorForProvidingHashValuesFromGeneratorOfTypeWithSubValue(
  type: ObjC.Type,
  propertyName: string,
): (attributeValueAccessor: string) => TypeEqualityValue[] {
  return function (attributeValueAccessor: string): TypeEqualityValue[] {
    const generationGroup: TypeEqualityGenerationGroup =
      generationGroupForType(type);
    return generationGroup.hashGenerator(
      attributeValueAccessor + '.' + propertyName,
    );
  };
}

function generatorForProvidingEqualityValuesFromGeneratorOfType(
  type: ObjC.Type,
): (attributeValueAccessor: string) => TypeEqualityValue[] {
  return function (attributeValueAccessor: string): TypeEqualityValue[] {
    const generationGroup: TypeEqualityGenerationGroup =
      generationGroupForType(type);
    return generationGroup.equalityCheckGenerator(attributeValueAccessor);
  };
}

function generatorForProvidingHashValuesFromGeneratorOfType(
  type: ObjC.Type,
): (attributeValueAccessor: string) => TypeEqualityValue[] {
  return function (attributeValueAccessor: string): TypeEqualityValue[] {
    const generationGroup: TypeEqualityGenerationGroup =
      generationGroupForType(type);
    return generationGroup.hashGenerator(attributeValueAccessor);
  };
}

interface GeneratedTypeEqualityInformation {
  equalityChecks: TypeEqualityValue[];
  hashValues: TypeEqualityValue[];
}

function generatedTypeEqualityInformationForAttribute(
  attribute: ObjectSpec.Attribute,
): GeneratedTypeEqualityInformation {
  const type: ObjC.Type = ObjectSpecCodeUtils.computeTypeOfAttribute(attribute);
  const generationGroup: TypeEqualityGenerationGroup =
    generationGroupForType(type);
  const attributeValueAccessor: string =
    ObjectSpecCodeUtils.ivarForAttribute(attribute);
  return generatedTypeEqualityInformationForGenerationGroup(
    generationGroup,
    attributeValueAccessor,
  );
}

function buildImportsToInclude(
  soFar: ObjC.Import[],
  generatedTypeEqualityInformation: GeneratedTypeEqualityInformation,
): ObjC.Import[] {
  return soFar
    .concat(
      generatedTypeEqualityInformation.equalityChecks.reduce(
        buildTypeEqualityValueImportsToInclude,
        [],
      ),
    )
    .concat(
      generatedTypeEqualityInformation.hashValues.reduce(
        buildTypeEqualityValueImportsToInclude,
        [],
      ),
    );
}

function buildEqualityChecks(
  soFar: TypeEqualityValue[],
  generatedTypeEqualityInformation: GeneratedTypeEqualityInformation,
): TypeEqualityValue[] {
  return soFar.concat(generatedTypeEqualityInformation.equalityChecks);
}

function stringForIncludingEqualityCheckInCode(equalityCheck: string): string {
  return '  ' + equalityCheck + ' &&';
}

enum ComparisonResult {
  OrderedAscending = -1,
  OrderedSame = 0,
  OrderedDescending = 1,
}

function compareTypeEqualityValuesByComputationCost(
  typeEqualityValue: TypeEqualityValue,
  typeEqualityValueToCompare: TypeEqualityValue,
): ComparisonResult {
  const baseComputationCostValue: number = computationCostAsNumber(
    typeEqualityValue.computationCost,
  );
  const comparisonComputationCostValue: number = computationCostAsNumber(
    typeEqualityValueToCompare.computationCost,
  );
  if (baseComputationCostValue < comparisonComputationCostValue) {
    return ComparisonResult.OrderedAscending;
  } else if (baseComputationCostValue > comparisonComputationCostValue) {
    return ComparisonResult.OrderedDescending;
  } else {
    return ComparisonResult.OrderedSame;
  }
}

function isEqualInstanceMethod(
  typeName: string,
  generatedTypeEqualityInformation: GeneratedTypeEqualityInformation[],
): ObjC.Method {
  const openingCode: string[] = [
    'if (self == object) {',
    '  return YES;',
    '} else if (object == nil || ![object isKindOfClass:[self class]]) {',
    '  return NO;',
    '}',
    'return',
  ];
  const equalityCheckEqualityValues: TypeEqualityValue[] =
    generatedTypeEqualityInformation.reduce(buildEqualityChecks, []);
  const equalityCheckEqualityValuesSortedByCost: TypeEqualityValue[] =
    equalityCheckEqualityValues
      .concat()
      .sort(compareTypeEqualityValuesByComputationCost);
  const equalityChecks: string[] =
    equalityCheckEqualityValuesSortedByCost.map(selectValue);
  const equalityChecksUpUntilLastOne: string[] = equalityChecks
    .slice(0, equalityChecks.length - 1)
    .map(stringForIncludingEqualityCheckInCode);
  const lastEqualityCheck: string = equalityChecks[equalityChecks.length - 1];
  const code: string[] = openingCode
    .concat(equalityChecksUpUntilLastOne)
    .concat('  ' + lastEqualityCheck + ';');
  return {
    preprocessors: [],
    belongsToProtocol: 'NSObject',
    keywords: [
      {
        name: 'isEqual',
        argument: {