Files
MRCC/launcher/PCL-CE/PCL.Core/Utils/SemVer.cs
T
xyou f70b061d1a
CI / Go Backend (push) Canceled after 0s
feat: 项目初始化 + 3D方块世界原型 + AI助搭系统
初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器

HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块

Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚

AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
2026-08-08 14:07:56 +08:00

175 lines
5.2 KiB
C#

using System;
using System.Text.RegularExpressions;
namespace PCL.Core.Utils;
[Serializable]
public class SemVer(int major, int minor, int patch, string? prerelease = null, string? buildMetadata = null)
: IComparable<SemVer>, IEquatable<SemVer>
{
public int Major => major;
public int Minor => minor;
public int Patch => patch;
public string Prerelease => prerelease ?? string.Empty;
public string BuildMetadata => buildMetadata ?? string.Empty;
public static SemVer Parse(string version)
{
if (!TryParse(version, out var result))
{
throw new ArgumentException("Invalid semantic version format");
}
return result!;
}
public static bool TryParse(string version, out SemVer? result)
{
if (string.IsNullOrWhiteSpace(version))
{
result = null;
return false;
}
var match = RegexPatterns.SemVer.Match(version);
if (!match.Success)
{
result = null;
return false;
}
result = _CreateFromMatch(match);
return true;
}
private static SemVer _CreateFromMatch(Match match)
{
var major = int.Parse(match.Groups["major"].Value);
var minor = int.Parse(match.Groups["minor"].Value);
var patch = int.Parse(match.Groups["patch"].Value);
var prerelease = match.Groups["prerelease"].Value;
var build = match.Groups["build"].Value;
return new SemVer(major, minor, patch, prerelease, build);
}
public int CompareTo(SemVer? other)
{
if (other is null) return 1;
var compare = Major.CompareTo(other.Major);
if (compare != 0) return compare;
compare = Minor.CompareTo(other.Minor);
if (compare != 0) return compare;
compare = Patch.CompareTo(other.Patch);
if (compare != 0) return compare;
return _ComparePrerelease(Prerelease, other.Prerelease);
}
private static int _ComparePrerelease(string a, string b)
{
if (string.Equals(a, b, StringComparison.Ordinal))
return 0;
// 正式版优先级高于预发布版
if (string.IsNullOrEmpty(a)) return 1;
if (string.IsNullOrEmpty(b)) return -1;
var identifiersA = a.Split('.');
var identifiersB = b.Split('.');
var minLength = Math.Min(identifiersA.Length, identifiersB.Length);
for (var i = 0; i < minLength; i++)
{
var idA = identifiersA[i];
var idB = identifiersB[i];
var aIsNumeric = int.TryParse(idA, out var numA);
var bIsNumeric = int.TryParse(idB, out var numB);
int result;
if (aIsNumeric && bIsNumeric)
{
result = numA.CompareTo(numB);
}
else if (aIsNumeric || bIsNumeric)
{
// 数值标识符比非数值标识符优先级低
result = aIsNumeric ? -1 : 1;
}
else
{
result = string.Compare(idA, idB, StringComparison.Ordinal);
}
if (result != 0)
return result;
}
return identifiersA.Length.CompareTo(identifiersB.Length);
}
public override string ToString()
{
var version = $"{Major}.{Minor}.{Patch}";
if (!string.IsNullOrEmpty(Prerelease))
version += $"-{Prerelease}";
if (!string.IsNullOrEmpty(BuildMetadata))
version += $"+{BuildMetadata}";
return version;
}
// 实现相等性比较
public override bool Equals(object? obj)
{
return Equals(obj as SemVer);
}
public bool Equals(SemVer? other)
{
return other is not null &&
Major == other.Major &&
Minor == other.Minor &&
Patch == other.Patch &&
string.Equals(Prerelease, other.Prerelease, StringComparison.Ordinal) &&
string.Equals(BuildMetadata, other.BuildMetadata, StringComparison.Ordinal);
}
public override int GetHashCode()
{
unchecked
{
var hash = 17;
hash = hash * 23 + Major.GetHashCode();
hash = hash * 23 + Minor.GetHashCode();
hash = hash * 23 + Patch.GetHashCode();
hash = hash * 23 + Prerelease.GetHashCode();
hash = hash * 23 + BuildMetadata.GetHashCode();
return hash;
}
}
// 运算符重载
public static bool operator ==(SemVer? left, SemVer? right)
{
if (ReferenceEquals(left, right)) return true;
if (left is null || right is null) return false;
return left.Equals(right);
}
public static bool operator !=(SemVer? left, SemVer? right) => !(left == right);
public static bool operator <(SemVer? left, SemVer? right) =>
left is null ? right is not null : left.CompareTo(right) < 0;
public static bool operator >(SemVer? left, SemVer? right) =>
left is not null && left.CompareTo(right) > 0;
public static bool operator <=(SemVer? left, SemVer? right) =>
left is null || left.CompareTo(right) <= 0;
public static bool operator >=(SemVer? left, SemVer? right) =>
left is null ? right is null : left.CompareTo(right) >= 0;
}