| // Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| import 'package:pub_semver/pub_semver.dart'; |
| |
| import '../exceptions.dart'; |
| import '../language_version.dart'; |
| import '../package_name.dart'; |
| import '../sdk.dart'; |
| import '../source/sdk.dart'; |
| import 'incompatibility.dart'; |
| |
| /// The reason an [Incompatibility]'s terms are incompatible. |
| sealed class IncompatibilityCause { |
| const IncompatibilityCause(); |
| |
| /// Human readable notice / information providing context for this |
| /// incompatibility. |
| /// |
| /// This may be multiple lines, and will be printed before the explanation. |
| /// This is used highlight information that is useful for understanding the |
| /// why this conflict happened. |
| String? get notice => null; |
| |
| /// Human readable hint indicating how this incompatibility may be resolved. |
| /// |
| /// This may be multiple lines, and will be printed after the explanation. |
| /// This should only be included if it is actionable and likely to resolve the |
| /// issue for the user. |
| String? get hint => null; |
| } |
| |
| /// The incompatibility represents the requirement that the root package |
| /// exists. |
| class RootIncompatibilityCause extends IncompatibilityCause { |
| factory RootIncompatibilityCause() => const RootIncompatibilityCause._(); |
| const RootIncompatibilityCause._(); |
| } |
| |
| /// The incompatibility represents a package's dependency. |
| class DependencyIncompatibilityCause extends IncompatibilityCause { |
| final PackageRange depender; |
| final PackageRange target; |
| DependencyIncompatibilityCause(this.depender, this.target); |
| |
| @override |
| String? get notice { |
| final dependerDescription = depender.description; |
| if (dependerDescription is SdkDescription) { |
| final targetConstraint = target.constraint; |
| if (targetConstraint is Version) { |
| return ''' |
| Note: ${target.name} is pinned to version $targetConstraint by ${depender.name} from the ${dependerDescription.sdk} SDK. |
| See https://dart.dev/go/sdk-version-pinning for details. |
| '''; |
| } |
| } |
| return null; |
| } |
| } |
| |
| /// The incompatibility indicates that the package has no versions that match |
| /// the given constraint. |
| class NoVersionsIncompatibilityCause extends IncompatibilityCause { |
| factory NoVersionsIncompatibilityCause() => |
| const NoVersionsIncompatibilityCause._(); |
| const NoVersionsIncompatibilityCause._(); |
| } |
| |
| /// The incompatibility indicates that the package has an unknown source. |
| class UnknownSourceIncompatibilityCause extends IncompatibilityCause { |
| factory UnknownSourceIncompatibilityCause() => |
| const UnknownSourceIncompatibilityCause._(); |
| const UnknownSourceIncompatibilityCause._(); |
| } |
| |
| /// The incompatibility was derived from two existing incompatibilities during |
| /// conflict resolution. |
| class ConflictCause extends IncompatibilityCause { |
| /// The incompatibility that was originally found to be in conflict, from |
| /// which the target incompatibility was derived. |
| final Incompatibility conflict; |
| |
| /// The incompatibility that caused the most recent satisfier for [conflict], |
| /// from which the target incompatibility was derived. |
| final Incompatibility other; |
| |
| ConflictCause(this.conflict, this.other); |
| } |
| |
| /// The incompatibility represents a package's SDK constraint being |
| /// incompatible with the current SDK. |
| class SdkIncompatibilityCause extends IncompatibilityCause { |
| /// The union of all the incompatible versions' constraints on the SDK. |
| // TODO(zarah): Investigate if this can be non-nullable |
| final VersionConstraint? constraint; |
| |
| /// The SDK with which the package was incompatible. |
| final Sdk sdk; |
| |
| bool get noNullSafetyCause => |
| sdk.isDartSdk && |
| !LanguageVersion.fromSdkConstraint(constraint).supportsNullSafety && |
| sdk.version! >= Version(3, 0, 0).firstPreRelease; |
| |
| @override |
| String? get notice { |
| // If the SDK is not available, then we have an actionable [hint] printed |
| // after the explanation. So we don't need to state that the SDK is not |
| // available. |
| if (!sdk.isAvailable) { |
| return null; |
| } |
| // If the SDK is available and we have an incompatibility, then the user has |
| // the wrong SDK version (one that is not compatible with any solution). |
| // Thus, it makes sense to highlight the current SDK version. |
| return 'The current ${sdk.name} SDK version is ${sdk.version}.'; |
| } |
| |
| @override |
| String? get hint { |
| if (noNullSafetyCause) { |
| return 'The lower bound of "sdk: \'$constraint\'" must be 2.12.0' |
| ' or higher to enable null safety.' |
| '\nFor details, see https://dart.dev/null-safety'; |
| } |
| // If the SDK is available, then installing it won't help |
| if (sdk.isAvailable) { |
| return null; |
| } |
| // Return an install message for the SDK, if there is an install message. |
| return sdk.installMessage; |
| } |
| |
| SdkIncompatibilityCause(this.constraint, this.sdk); |
| } |
| |
| /// The incompatibility represents a package that couldn't be found by its |
| /// source. |
| class PackageNotFoundIncompatibilityCause extends IncompatibilityCause { |
| /// The exception indicating why the package couldn't be found. |
| final PackageNotFoundException exception; |
| |
| PackageNotFoundIncompatibilityCause(this.exception); |
| |
| @override |
| String? get hint => exception.hint; |
| } |
| |
| /// The incompatibility represents a package-version that is not allowed to be |
| /// used in the solve for some external reason. |
| class PackageVersionForbiddenCause extends IncompatibilityCause { |
| /// The reason this package version was forbidden. |
| final String? reason; |
| |
| PackageVersionForbiddenCause({this.reason}); |
| |
| @override |
| String? get hint => reason; |
| } |