Files
Installer/Editor/ShrinkPackageVersion.cs

118 lines
4.2 KiB
C#

#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; }
}
}
}