mirror of
https://github.com/2dust/v2rayN.git
synced 2026-10-06 21:42:08 +03:00
Lock sing-box max version (#10091)
* Refactor Semantic Version * Lock max version * Allow sing-box update to pre-release
This commit is contained in:
@@ -1,18 +1,20 @@
|
||||
namespace ServiceLib.Models.Dto;
|
||||
|
||||
public class SemanticVersion
|
||||
public class SemanticVersion : IEquatable<SemanticVersion>, IComparable
|
||||
{
|
||||
private readonly string? build;
|
||||
private readonly int major;
|
||||
private readonly int minor;
|
||||
private readonly int patch;
|
||||
private readonly string version;
|
||||
private readonly string? prerelease;
|
||||
private readonly string raw;
|
||||
|
||||
public SemanticVersion(int major, int minor, int patch)
|
||||
{
|
||||
this.major = major;
|
||||
this.minor = minor;
|
||||
this.patch = patch;
|
||||
version = $"{major}.{minor}.{patch}";
|
||||
raw = $"{major}.{minor}.{patch}";
|
||||
}
|
||||
|
||||
public SemanticVersion(string? version)
|
||||
@@ -24,26 +26,41 @@ public class SemanticVersion
|
||||
major = 0;
|
||||
minor = 0;
|
||||
patch = 0;
|
||||
raw = $"{major}.{minor}.{patch}";
|
||||
return;
|
||||
}
|
||||
this.version = version.RemovePrefix('v');
|
||||
raw = version;
|
||||
|
||||
var parts = this.version.Split('.');
|
||||
if (parts.Length == 2)
|
||||
var span = version.StartsWith("v", StringComparison.OrdinalIgnoreCase)
|
||||
? version.AsSpan(1)
|
||||
: version.AsSpan();
|
||||
var plusIdx = span.IndexOf('+');
|
||||
var metadataSpan = plusIdx >= 0 ? span[(plusIdx + 1)..] : [];
|
||||
var leftOfPlus = plusIdx >= 0 ? span[..plusIdx] : span;
|
||||
var dashIdx = leftOfPlus.IndexOf('-');
|
||||
var versionSpan = dashIdx >= 0 ? leftOfPlus[..dashIdx] : leftOfPlus;
|
||||
var preReleaseSpan = dashIdx >= 0 ? leftOfPlus[(dashIdx + 1)..] : [];
|
||||
|
||||
build = metadataSpan.Length > 0 ? metadataSpan.ToString() : null;
|
||||
prerelease = preReleaseSpan.Length > 0 ? preReleaseSpan.ToString() : null;
|
||||
|
||||
var parts = versionSpan.ToString().Split('.');
|
||||
switch (parts.Length)
|
||||
{
|
||||
major = int.Parse(parts.First());
|
||||
minor = int.Parse(parts.Last());
|
||||
patch = 0;
|
||||
}
|
||||
else if (parts.Length is 3 or 4)
|
||||
{
|
||||
major = int.Parse(parts[0]);
|
||||
minor = int.Parse(parts[1]);
|
||||
patch = int.Parse(parts[2]);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException("Invalid version string");
|
||||
case 2:
|
||||
major = int.Parse(parts.First());
|
||||
minor = int.Parse(parts.Last());
|
||||
patch = 0;
|
||||
break;
|
||||
|
||||
case 3 or 4:
|
||||
major = int.Parse(parts[0]);
|
||||
minor = int.Parse(parts[1]);
|
||||
patch = int.Parse(parts[2]);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new ArgumentException("Invalid version string");
|
||||
}
|
||||
}
|
||||
catch
|
||||
@@ -51,135 +68,186 @@ public class SemanticVersion
|
||||
major = 0;
|
||||
minor = 0;
|
||||
patch = 0;
|
||||
raw = $"{major}.{minor}.{patch}";
|
||||
}
|
||||
}
|
||||
|
||||
public bool Equals(SemanticVersion? other)
|
||||
{
|
||||
if (other is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (ReferenceEquals(this, other))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return major == other.major && minor == other.minor && patch == other.patch && prerelease == other.prerelease;
|
||||
}
|
||||
|
||||
public static bool TryParse(string? version, out SemanticVersion? result)
|
||||
{
|
||||
try
|
||||
{
|
||||
result = new SemanticVersion(version);
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
result = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
if (obj is SemanticVersion other)
|
||||
{
|
||||
return major == other.major && minor == other.minor && patch == other.patch;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return obj is SemanticVersion other && Equals(other);
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return major.GetHashCode() ^ minor.GetHashCode() ^ patch.GetHashCode();
|
||||
return HashCode.Combine(major, minor, patch, prerelease);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use ToVersionString(string? prefix) instead if possible.
|
||||
/// </summary>
|
||||
/// <returns>major.minor.patch</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return version;
|
||||
return raw;
|
||||
}
|
||||
|
||||
public string ToVersionString(string? prefix = null)
|
||||
public string ToStandardVersionString(string? prefix = null)
|
||||
{
|
||||
if (prefix == null)
|
||||
var sb = new StringBuilder();
|
||||
if (!string.IsNullOrEmpty(prefix))
|
||||
{
|
||||
return version;
|
||||
sb.Append(prefix);
|
||||
}
|
||||
else
|
||||
sb.Append($"{major}.{minor}.{patch}");
|
||||
if (!string.IsNullOrEmpty(prerelease))
|
||||
{
|
||||
return $"{prefix}{version}";
|
||||
sb.Append($"-{prerelease}");
|
||||
}
|
||||
if (!string.IsNullOrEmpty(build))
|
||||
{
|
||||
sb.Append($"+{build}");
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
public static bool operator ==(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.Equals(v2); }
|
||||
public static bool operator <(SemanticVersion left, SemanticVersion right)
|
||||
{
|
||||
return left.CompareTo(right) < 0;
|
||||
}
|
||||
|
||||
public static bool operator !=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return !v1.Equals(v2); }
|
||||
public static bool operator >(SemanticVersion left, SemanticVersion right)
|
||||
{
|
||||
return left.CompareTo(right) > 0;
|
||||
}
|
||||
|
||||
public static bool operator >=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.GreaterEquals(v2); }
|
||||
public static bool operator <=(SemanticVersion left, SemanticVersion right)
|
||||
{
|
||||
return left.CompareTo(right) <= 0;
|
||||
}
|
||||
|
||||
public static bool operator <=(SemanticVersion v1, SemanticVersion v2)
|
||||
{ return v1.LessEquals(v2); }
|
||||
public static bool operator >=(SemanticVersion left, SemanticVersion right)
|
||||
{
|
||||
return left.CompareTo(right) >= 0;
|
||||
}
|
||||
|
||||
#region Private
|
||||
|
||||
private bool GreaterEquals(SemanticVersion other)
|
||||
public int CompareTo(SemanticVersion other)
|
||||
{
|
||||
if (major < other.major)
|
||||
if (major != other.major)
|
||||
{
|
||||
return false;
|
||||
return major.CompareTo(other.major);
|
||||
}
|
||||
else if (major > other.major)
|
||||
if (minor != other.minor)
|
||||
{
|
||||
return true;
|
||||
return minor.CompareTo(other.minor);
|
||||
}
|
||||
else
|
||||
if (patch != other.patch)
|
||||
{
|
||||
if (minor < other.minor)
|
||||
return patch.CompareTo(other.patch);
|
||||
}
|
||||
return ComparePreRelease(prerelease, other.prerelease);
|
||||
}
|
||||
|
||||
public int CompareTo(object? obj)
|
||||
{
|
||||
if (obj is null)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (obj is SemanticVersion other)
|
||||
{
|
||||
return CompareTo(other);
|
||||
}
|
||||
throw new ArgumentException("Object is not a SemanticVersion");
|
||||
}
|
||||
|
||||
private static int ComparePreRelease(string? left, string? right)
|
||||
{
|
||||
if (string.IsNullOrEmpty(left) && string.IsNullOrEmpty(right))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (string.IsNullOrEmpty(left))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (string.IsNullOrEmpty(right))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
var leftSpan = left.AsSpan();
|
||||
var rightSpan = right.AsSpan();
|
||||
using var leftEnum = leftSpan.Split('.').GetEnumerator();
|
||||
using var rightEnum = rightSpan.Split('.').GetEnumerator();
|
||||
|
||||
while (true)
|
||||
{
|
||||
var hasLeft = leftEnum.MoveNext();
|
||||
var hasRight = rightEnum.MoveNext();
|
||||
|
||||
if (!hasLeft && !hasRight)
|
||||
{
|
||||
return false;
|
||||
return 0;
|
||||
}
|
||||
else if (minor > other.minor)
|
||||
if (!hasLeft)
|
||||
{
|
||||
return true;
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
if (!hasRight)
|
||||
{
|
||||
if (patch < other.patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (patch > other.patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
var leftSegment = leftSpan[leftEnum.Current];
|
||||
var rightSegment = rightSpan[rightEnum.Current];
|
||||
|
||||
var segCmp = CompareSegment(leftSegment, rightSegment);
|
||||
if (segCmp != 0)
|
||||
{
|
||||
return segCmp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool LessEquals(SemanticVersion other)
|
||||
private static int CompareSegment(ReadOnlySpan<char> left, ReadOnlySpan<char> right)
|
||||
{
|
||||
if (major < other.major)
|
||||
var leftIsNum = ulong.TryParse(left, out var leftNum);
|
||||
var rightIsNum = ulong.TryParse(right, out var rightNum);
|
||||
|
||||
return (leftIsNum, rightIsNum) switch
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (major > other.major)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (minor < other.minor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (minor > other.minor)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (patch < other.patch)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (patch > other.patch)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
(true, true) => leftNum.CompareTo(rightNum),
|
||||
|
||||
(false, true) => 1,
|
||||
(true, false) => -1,
|
||||
|
||||
(false, false) => left.SequenceCompareTo(right),
|
||||
};
|
||||
}
|
||||
|
||||
#endregion Private
|
||||
|
||||
Reference in New Issue
Block a user