初始化 monorepo: Go后端(7微服务) + Unity客户端(9模块) + 启动器 HTML5原型: Three.js 3D体素世界, Perlin噪声地形, 原版材质, 22种方块 Minecraft创造模式背包: 双栏布局, 拖拽移动物品, 方向性元件引脚 AI助搭策划文档 + 客户端/服务端骨架 + Docker Compose + CI
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
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;
|
||||
}
|
||||
Reference in New Issue
Block a user