fix: compare installed package versions semantically
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
#nullable enable
|
||||
|
||||
using System;
|
||||
|
||||
namespace ShrinkSDK.Installer
|
||||
{
|
||||
public enum ShrinkPackageVersionRelation
|
||||
{
|
||||
Unknown,
|
||||
Older,
|
||||
Equal,
|
||||
Newer
|
||||
}
|
||||
|
||||
public static class ShrinkPackageVersion
|
||||
{
|
||||
public static ShrinkPackageVersionRelation Classify(string? installedVersion, string? targetVersion)
|
||||
{
|
||||
return TryCompare(installedVersion, targetVersion, out var comparison)
|
||||
? comparison < 0
|
||||
? ShrinkPackageVersionRelation.Older
|
||||
: comparison > 0
|
||||
? ShrinkPackageVersionRelation.Newer
|
||||
: ShrinkPackageVersionRelation.Equal
|
||||
: ShrinkPackageVersionRelation.Unknown;
|
||||
}
|
||||
|
||||
public static bool TryCompare(string? left, string? right, out int comparison)
|
||||
{
|
||||
comparison = 0;
|
||||
if (!TryParse(left, out var leftVersion) || !TryParse(right, out var rightVersion))
|
||||
return false;
|
||||
|
||||
for (var index = 0; index < 3; index++)
|
||||
{
|
||||
comparison = leftVersion.Core[index].CompareTo(rightVersion.Core[index]);
|
||||
if (comparison != 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
comparison = ComparePrerelease(leftVersion.Prerelease, rightVersion.Prerelease);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParse(string? raw, out ParsedVersion version)
|
||||
{
|
||||
version = default;
|
||||
if (string.IsNullOrWhiteSpace(raw))
|
||||
return false;
|
||||
|
||||
var value = raw.Trim();
|
||||
if (value.StartsWith("v", StringComparison.OrdinalIgnoreCase))
|
||||
value = value.Substring(1);
|
||||
var buildIndex = value.IndexOf('+');
|
||||
if (buildIndex >= 0)
|
||||
value = value.Substring(0, buildIndex);
|
||||
var prereleaseIndex = value.IndexOf('-');
|
||||
var coreText = prereleaseIndex >= 0 ? value.Substring(0, prereleaseIndex) : value;
|
||||
var prerelease = prereleaseIndex >= 0 ? value.Substring(prereleaseIndex + 1) : string.Empty;
|
||||
var coreParts = coreText.Split('.');
|
||||
if (coreParts.Length is < 1 or > 3)
|
||||
return false;
|
||||
|
||||
var core = new int[3];
|
||||
for (var index = 0; index < coreParts.Length; index++)
|
||||
{
|
||||
if (!int.TryParse(coreParts[index], out core[index]) || core[index] < 0)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (prereleaseIndex >= 0 && string.IsNullOrWhiteSpace(prerelease))
|
||||
return false;
|
||||
version = new ParsedVersion(core, prerelease);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static int ComparePrerelease(string left, string right)
|
||||
{
|
||||
var leftEmpty = string.IsNullOrEmpty(left);
|
||||
var rightEmpty = string.IsNullOrEmpty(right);
|
||||
if (leftEmpty || rightEmpty)
|
||||
return leftEmpty == rightEmpty ? 0 : leftEmpty ? 1 : -1;
|
||||
|
||||
var leftParts = left.Split('.');
|
||||
var rightParts = right.Split('.');
|
||||
var count = Math.Min(leftParts.Length, rightParts.Length);
|
||||
for (var index = 0; index < count; index++)
|
||||
{
|
||||
var leftNumeric = int.TryParse(leftParts[index], out var leftValue);
|
||||
var rightNumeric = int.TryParse(rightParts[index], out var rightValue);
|
||||
int comparison;
|
||||
if (leftNumeric && rightNumeric)
|
||||
comparison = leftValue.CompareTo(rightValue);
|
||||
else if (leftNumeric != rightNumeric)
|
||||
comparison = leftNumeric ? -1 : 1;
|
||||
else
|
||||
comparison = string.Compare(leftParts[index], rightParts[index], StringComparison.Ordinal);
|
||||
if (comparison != 0)
|
||||
return comparison;
|
||||
}
|
||||
|
||||
return leftParts.Length.CompareTo(rightParts.Length);
|
||||
}
|
||||
|
||||
private readonly struct ParsedVersion
|
||||
{
|
||||
public ParsedVersion(int[] core, string prerelease)
|
||||
{
|
||||
Core = core;
|
||||
Prerelease = prerelease;
|
||||
}
|
||||
|
||||
public int[] Core { get; }
|
||||
public string Prerelease { get; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user