compareTo method

List<ApiChange> compareTo(
  1. DocMethod newMethod, {
  2. required DocComponent component,
})

Compares this method with newMethod and returns a list of ApiChanges that have been detected between the two methods.

Implementation

List<ApiChange> compareTo(
  DocMethod newMethod, {
  required DocComponent component,
}) {
  final changes = <ApiChange>[];
  _addChange(DocParameter parameter, ApiChangeOperation operation, {String? oldName, String? annotation}) {
    changes.add(MethodParameterApiChange(
      component: component,
      method: this,
      operation: operation,
      parameter: parameter,
      oldName: oldName,
      annotation: annotation,
    ));
  }

  void _checkParamAnnotations(DocParameter oldP, DocParameter newP) {
    for (final annotation in oldP.annotations) {
      if (!newP.annotations.contains(annotation)) {
        _addChange(oldP, ApiChangeOperation.annotationRemoved, annotation: annotation);
      }
    }
    for (final annotation in newP.annotations) {
      if (!oldP.annotations.contains(annotation)) {
        _addChange(oldP, ApiChangeOperation.annotationAdded, annotation: annotation);
      }
    }
  }

  if (returnType != newMethod.returnType) {
    changes.add(MethodApiChange(
      component: component,
      method: this,
      operation: ApiChangeOperation.typeChanged,
      newType: newMethod.returnType,
    ));
  }

  for (final annotation in annotations) {
    if (!newMethod.annotations.contains(annotation)) {
      changes.add(MethodApiChange(
        component: component,
        method: this,
        operation: ApiChangeOperation.annotationRemoved,
        annotation: annotation,
      ));
    }
  }
  for (final annotation in newMethod.annotations) {
    if (!annotations.contains(annotation)) {
      changes.add(MethodApiChange(
        component: component,
        method: this,
        operation: ApiChangeOperation.annotationAdded,
        annotation: annotation,
      ));
    }
  }

  final oldPositional = signature.where((p) => !p.named).toList();
  final oldNamed = signature.where((p) => p.named).toList();
  final newPositional = newMethod.signature.where((p) => !p.named).toList();
  final newNamed = newMethod.signature.where((p) => p.named).toList();

  final processedOld = <DocParameter>{};
  final processedNew = <DocParameter>{};

  // Check old named -> new positional
  for (final oldP in oldNamed) {
    final newP = newPositional.firstWhereOrNull((p) => p.name == oldP.name);
    if (newP != null) {
      _addChange(oldP, ApiChangeOperation.becamePositional);
      if (oldP.type != newP.type) {
        _addChange(oldP, ApiChangeOperation.typeChanged);
      }
      if (oldP.required != newP.required) {
        _addChange(
          oldP,
          oldP.required ? ApiChangeOperation.becameOptional : ApiChangeOperation.becameRequired,
        );
      }
      _checkParamAnnotations(oldP, newP);
      processedOld.add(oldP);
      processedNew.add(newP);
    }
  }

  // Check old positional -> new named
  for (final oldP in oldPositional) {
    final newP = newNamed.firstWhereOrNull((p) => p.name == oldP.name);
    if (newP != null) {
      _addChange(oldP, ApiChangeOperation.becameNamed);
      if (oldP.type != newP.type) {
        _addChange(oldP, ApiChangeOperation.typeChanged);
      }
      if (oldP.required != newP.required) {
        _addChange(
          oldP,
          oldP.required ? ApiChangeOperation.becameOptional : ApiChangeOperation.becameRequired,
        );
      }
      _checkParamAnnotations(oldP, newP);
      processedOld.add(oldP);
      processedNew.add(newP);
    }
  }

  // Compare remaining named parameters
  for (final oldP in oldNamed) {
    if (processedOld.contains(oldP)) continue;
    final newP = newNamed.firstWhereOrNull((p) => p.name == oldP.name);
    if (newP == null) {
      _addChange(oldP, ApiChangeOperation.removed);
    } else {
      processedNew.add(newP);
      if (oldP.type != newP.type) {
        _addChange(oldP, ApiChangeOperation.typeChanged);
      }
      if (oldP.required != newP.required) {
        _addChange(
          oldP,
          oldP.required ? ApiChangeOperation.becameOptional : ApiChangeOperation.becameRequired,
        );
      }
      _checkParamAnnotations(oldP, newP);
    }
  }

  for (final newP in newNamed) {
    if (processedNew.contains(newP)) continue;
    _addChange(newP, ApiChangeOperation.added);
  }

  // Compare remaining positional parameters by index
  final remainingOldPositional = oldPositional.where((p) => !processedOld.contains(p)).toList();
  final remainingNewPositional = newPositional.where((p) => !processedNew.contains(p)).toList();

  final maxPos = remainingOldPositional.length > remainingNewPositional.length
      ? remainingOldPositional.length
      : remainingNewPositional.length;

  for (var i = 0; i < maxPos; i++) {
    if (i < remainingOldPositional.length && i < remainingNewPositional.length) {
      final oldP = remainingOldPositional[i];
      final newP = remainingNewPositional[i];

      if (oldP.name != newP.name) {
        _addChange(newP, ApiChangeOperation.renamed, oldName: oldP.name);
      }
      if (oldP.type != newP.type) {
        _addChange(oldP, ApiChangeOperation.typeChanged);
      }
      if (oldP.required != newP.required) {
        _addChange(
          oldP,
          oldP.required ? ApiChangeOperation.becameOptional : ApiChangeOperation.becameRequired,
        );
      }
      _checkParamAnnotations(oldP, newP);
    } else if (i >= remainingOldPositional.length) {
      _addChange(remainingNewPositional[i], ApiChangeOperation.added);
    } else {
      _addChange(remainingOldPositional[i], ApiChangeOperation.removed);
    }
  }

  return changes;
}