Skip to content

Instantly share code, notes, and snippets.

@emgarten
Created February 25, 2015 07:56
Show Gist options
  • Save emgarten/e1d6bd5dd3bbfb1ae4e2 to your computer and use it in GitHub Desktop.
Save emgarten/e1d6bd5dd3bbfb1ae4e2 to your computer and use it in GitHub Desktop.
using System;
namespace NuGet.Versioning
{
/// <summary>
/// VersionFloatComparer orders versions around a floating range. The range in this
/// case is only the floating part.
/// Ex: [1.0.*, 2.0.0) has a floating range of [1.0.0, 1.1.0)
/// Filtering on the [1.0.0, 2.0.0) range needs to happen outside of this class, it will not be
/// taken into account here.
///
/// Ordering:
/// 1. The highest version that satisfies the range.
/// 2. The lowest version above the range is ordered next.
/// 3. The highest version below the range is after that.
///
/// #3 Should always be filtered out first since the lower bound used to describe the floating range is a hard minimum.
/// </summary>
public class VersionFloatComparer : IVersionFloatComparer
{
private readonly IVersionComparer _versionComparer;
private readonly VersionRangeBase _floatRange;
/// <summary>
/// Sorts a set of versions based on a floating range.
/// </summary>
/// <param name="floatRange">The floating part of the range. Ex: [1.0.*, 2.0.0) has a floating range of [1.0.0, 1.1.0)</param>
public VersionFloatComparer(VersionRangeBase floatRange)
: this(floatRange, VersionComparer.Default)
{
}
/// <summary>
/// Sorts a set of versions based on a floating range.
/// </summary>
/// <param name="floatRange">The floating part of the range. Ex: [1.0.*, 2.0.0) has a floating range of [1.0.0, 1.1.0)</param>
/// <param name="versionComparer">NuGetVersion comparer</param>
public VersionFloatComparer(VersionRangeBase floatRange, IVersionComparer versionComparer)
{
if (floatRange == null)
{
throw new ArgumentNullException("floatRange");
}
if (versionComparer == null)
{
throw new ArgumentNullException("IVersionComparer");
}
if (!floatRange.HasLowerBound)
{
throw new ArgumentException("floatRange must have a lower bound");
}
_floatRange = floatRange;
_versionComparer = versionComparer;
}
/// <summary>
/// Order versions based on which is the best match for the floating version.
/// The first item in the sorted list will be the best match, last is worst.
/// </summary>
public int Compare(NuGetVersion x, NuGetVersion y)
{
if (Object.ReferenceEquals(x, y))
{
return 0;
}
// null versions should not exist, but if they do they go at the end
if (Object.ReferenceEquals(y, null))
{
return -1;
}
if (Object.ReferenceEquals(x, null))
{
return 1;
}
// check if either version is in the floating range
bool xInRange = _floatRange.Satisfies(x, _versionComparer);
bool yInRange = _floatRange.Satisfies(x, _versionComparer);
if (xInRange && !yInRange)
{
// take the version in the range
return -1;
}
else if(yInRange && !xInRange)
{
// take the version in the range
return 1;
}
else if (xInRange && yInRange)
{
// compare in reverse to prefer the highest one
return _versionComparer.Compare(y, x);
}
else
{
// neither are in range
int xToLower = _versionComparer.Compare(x, _floatRange.MinVersion);
int yToLower = _versionComparer.Compare(y, _floatRange.MinVersion);
if (xToLower < 0 && yToLower > 0)
{
// favor the version above the range
return 1;
}
else if (xToLower > 0 && yToLower < 0)
{
// favor the version above the range
return -1;
}
else if (xToLower > 0 && yToLower > 0)
{
// favor the lower version if we are above the range
return _versionComparer.Compare(x, y);
}
else if (xToLower < 0 && yToLower < 0)
{
// favor the higher version if we are below the range
return _versionComparer.Compare(y, x);
}
}
// favor the higher version if nothing else works
// this may also be hit if both versions are equal
return _versionComparer.Compare(y, x);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment