src/NuGet.Core/NuGet.Versioning/VersionRangeBase.cs (222 lines of code) (raw):

// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Diagnostics.CodeAnalysis; namespace NuGet.Versioning { /// <summary> /// A base version range that handles ranges only and not any of the preferred version logic. /// </summary> public abstract class VersionRangeBase : IEquatable<VersionRangeBase> { private readonly bool _includeMinVersion; private readonly bool _includeMaxVersion; private readonly NuGetVersion? _minVersion; private readonly NuGetVersion? _maxVersion; /// <summary> /// Creates a VersionRange with the given min and max. /// </summary> /// <param name="minVersion">Lower bound of the version range.</param> /// <param name="includeMinVersion">True if minVersion satisfies the condition.</param> /// <param name="maxVersion">Upper bound of the version range.</param> /// <param name="includeMaxVersion">True if maxVersion satisfies the condition.</param> protected VersionRangeBase( NuGetVersion? minVersion = null, bool includeMinVersion = true, NuGetVersion? maxVersion = null, bool includeMaxVersion = false) { _minVersion = minVersion; _maxVersion = maxVersion; _includeMinVersion = includeMinVersion; _includeMaxVersion = includeMaxVersion; } /// <summary> /// True if MinVersion exists; /// </summary> [MemberNotNullWhen(true, nameof(MinVersion))] public bool HasLowerBound { get { return _minVersion != null; } } /// <summary> /// True if MaxVersion exists. /// </summary> [MemberNotNullWhen(true, nameof(MaxVersion))] public bool HasUpperBound { get { return _maxVersion != null; } } /// <summary> /// True if both MinVersion and MaxVersion exist. /// </summary> [MemberNotNullWhen(true, nameof(MinVersion))] [MemberNotNullWhen(true, nameof(MaxVersion))] public bool HasLowerAndUpperBounds { get { return HasLowerBound && HasUpperBound; } } /// <summary> /// True if MinVersion exists and is included in the range. /// </summary> [MemberNotNullWhen(true, nameof(MaxVersion))] public bool IsMinInclusive { get { return HasLowerBound && _includeMinVersion; } } /// <summary> /// True if MaxVersion exists and is included in the range. /// </summary> [MemberNotNullWhen(true, nameof(MaxVersion))] public bool IsMaxInclusive { get { return HasUpperBound && _includeMaxVersion; } } /// <summary> /// Maximum version allowed by this range. /// </summary> public NuGetVersion? MaxVersion { get { return _maxVersion; } } /// <summary> /// Minimum version allowed by this range. /// </summary> public NuGetVersion? MinVersion { get { return _minVersion; } } /// <summary> /// Determines if an NuGetVersion meets the requirements. /// </summary> /// <param name="version">SemVer to compare</param> /// <returns>True if the given version meets the version requirements.</returns> public bool Satisfies(NuGetVersion version) { // ignore metadata by default when finding a range. return Satisfies(version, VersionComparer.VersionRelease); } /// <summary> /// Determines if an NuGetVersion meets the requirements using the given mode. /// </summary> /// <param name="version">SemVer to compare</param> /// <param name="versionComparison">VersionComparison mode used to determine the version range.</param> /// <returns>True if the given version meets the version requirements.</returns> public bool Satisfies(NuGetVersion version, VersionComparison versionComparison) { return Satisfies(version, VersionComparer.Get(versionComparison)); } /// <summary> /// Determines if an NuGetVersion meets the requirements using the version comparer. /// </summary> /// <param name="version">SemVer to compare.</param> /// <param name="comparer">Version comparer used to determine if the version criteria is met.</param> /// <returns>True if the given version meets the version requirements.</returns> public bool Satisfies(NuGetVersion version, IVersionComparer comparer) { if (version == null) { throw new ArgumentNullException(nameof(version)); } if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } // Determine if version is in the given range using the comparer. var condition = true; if (HasLowerBound) { if (IsMinInclusive) { #pragma warning disable CS8604 // Possible null reference argument. // BCL is missing nullable annotations before net5.0 condition &= comparer.Compare(MinVersion, version) <= 0; } else { condition &= comparer.Compare(MinVersion, version) < 0; } } if (HasUpperBound) { if (IsMaxInclusive) { condition &= comparer.Compare(MaxVersion, version) >= 0; } else { condition &= comparer.Compare(MaxVersion, version) > 0; #pragma warning restore CS8604 // Possible null reference argument. } } return condition; } /// <summary> /// Compares the object as a VersionRange with the default comparer /// </summary> public override bool Equals(object? obj) { var range = obj as VersionRangeBase; if (range != null) { return VersionRangeComparer.Default.Equals(this, range); } return false; } /// <summary> /// Returns the hash code using the default comparer. /// </summary> public override int GetHashCode() { return VersionRangeComparer.Default.GetHashCode(this); } /// <summary> /// Default compare /// </summary> public bool Equals(VersionRangeBase? other) { return Equals(other, VersionRangeComparer.Default); } /// <summary> /// Use the VersionRangeComparer for equality checks /// </summary> public bool Equals(VersionRangeBase? other, IVersionRangeComparer comparer) { if (comparer == null) { throw new ArgumentNullException(nameof(comparer)); } #pragma warning disable CS8604 // Possible null reference argument. // IEqualityComparer<T>.Equals doesn't have nullable annotations before .NET 5.0 return comparer.Equals(this, other); #pragma warning restore CS8604 // Possible null reference argument. } /// <summary> /// Use a specific VersionComparison for comparison /// </summary> public bool Equals(VersionRangeBase? other, VersionComparison versionComparison) { IVersionRangeComparer comparer = VersionRangeComparer.Get(versionComparison); return Equals(other, comparer); } /// <summary> /// Use a specific IVersionComparer for comparison /// </summary> public bool Equals(VersionRangeBase? other, IVersionComparer versionComparer) { IVersionRangeComparer comparer = new VersionRangeComparer(versionComparer); return Equals(other, comparer); } /// <summary> /// SubSet check /// </summary> public bool IsSubSetOrEqualTo(VersionRangeBase? possibleSuperSet) { return IsSubSetOrEqualTo(possibleSuperSet, VersionComparer.Default); } /// <summary> /// SubSet check /// </summary> public bool IsSubSetOrEqualTo(VersionRangeBase? possibleSuperSet, IVersionComparer comparer) { var rangeComparer = new VersionRangeComparer(comparer); var possibleSubSet = this; var target = possibleSuperSet; if (rangeComparer.Equals(possibleSubSet, VersionRange.None)) { return true; } #pragma warning disable CS8604 // Possible null reference argument. // BCL doesn't have nullable annotations for IEqualityComparer<T> before net5.0 if (rangeComparer.Equals(target, VersionRange.None)) { return false; } #pragma warning restore CS8604 // Possible null reference argument. target ??= VersionRange.All; possibleSubSet ??= VersionRange.All; var result = true; if (possibleSubSet.HasLowerBound) { // normal check if (!target.Satisfies(possibleSubSet.MinVersion)) { // it's possible we didn't need that version, do a special non inclusive check if (!possibleSubSet.IsMinInclusive && !target.IsMinInclusive) { #pragma warning disable CS8604 // Possible null reference argument. // BCL missing annotations on IEqualityComparer<T> before .NET 5 result &= comparer.Equals(target.MinVersion, possibleSubSet.MinVersion); #pragma warning restore CS8604 // Possible null reference argument. } else { result = false; } } } else { result &= !target.HasLowerBound; } if (possibleSubSet.HasUpperBound) { // normal check if (!target.Satisfies(possibleSubSet.MaxVersion)) { // it's possible we didn't need that version, do a special non inclusive check if (!possibleSubSet.IsMaxInclusive && !target.IsMaxInclusive) { #pragma warning disable CS8604 // Possible null reference argument. // BCL is missing nullable annotations for IEqualityComparer<T> before net5.0 result &= comparer.Equals(target.MaxVersion, possibleSubSet.MaxVersion); #pragma warning restore CS8604 // Possible null reference argument. } else { result = false; } } } else { result &= !target.HasUpperBound; } return result; } /// <summary> /// Infer if the range should allow prerelease versions based on if the lower or upper bounds /// contain prerelease labels. /// </summary> protected bool HasPrereleaseBounds { get { return IsPrerelease(_minVersion) == true || IsPrerelease(_maxVersion) == true; } } private static bool? IsPrerelease(SemanticVersion? version) { bool? b = null; if (version != null) { b = version.IsPrerelease; } return b; } } }