A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Text.RegularExpressions;
4
5namespace Unity.VisualScripting
6{
7 public struct SemanticVersion : IComparable<SemanticVersion>
8 {
9 [Serialize]
10 public readonly int major;
11
12 [Serialize]
13 public readonly int minor;
14
15 [Serialize]
16 public readonly int patch;
17
18 [Serialize]
19 public readonly string label;
20
21 [Serialize]
22 public readonly int increment;
23
24 public SemanticVersion(int major, int minor, int patch, string label, int increment)
25 {
26 this.major = major;
27 this.minor = minor;
28 this.patch = patch;
29 this.label = label;
30 this.increment = increment;
31 }
32
33 public SemanticVersion(string semVerString)
34 {
35 this = Parse(semVerString);
36 }
37
38 public SemanticLabel semanticLabel
39 {
40 get
41 {
42 if (StringUtility.IsNullOrWhiteSpace(label))
43 {
44 return SemanticLabel.Unspecified;
45 }
46
47 switch (label.Filter(whitespace: false, punctuation: false, symbols: false).ToLower())
48 {
49 case "pre":
50 return SemanticLabel.Pre;
51
52 case "a":
53 case "alpha":
54 return SemanticLabel.Alpha;
55
56 case "b":
57 case "beta":
58 return SemanticLabel.Beta;
59
60 case "rc":
61 case "releasecandidate":
62 return SemanticLabel.ReleaseCandidate;
63
64 case "f":
65 case "final":
66 case "hotfix":
67 case "fix":
68 return SemanticLabel.Final;
69
70 default:
71 return SemanticLabel.Unspecified;
72 }
73 }
74 }
75
76 public override string ToString()
77 {
78 if (semanticLabel == SemanticLabel.Unspecified)
79 {
80 return $"{major}.{minor}.{patch}";
81 }
82 else
83 {
84 return $"{major}.{minor}.{patch}{label}{increment}";
85 }
86 }
87
88 public static implicit operator SemanticVersion(string s)
89 {
90 return Parse(s);
91 }
92
93 public static SemanticVersion Parse(string s)
94 {
95 SemanticVersion result;
96
97 if (!TryParse(s, out result))
98 {
99 throw new ArgumentException("s");
100 }
101
102 return result;
103 }
104
105 public static bool TryParse(string s, out SemanticVersion result)
106 {
107 result = default(SemanticVersion);
108
109 if (s == null)
110 {
111 throw new ArgumentNullException(nameof(s));
112 }
113
114 var regex = new Regex(@"(?<major>\d+)\.(?<minor>\d+)\.(?<patch>\d+)(?:(?<label>[a-zA-Z\s\-_\.]+)(?<increment>\d+))?", RegexOptions.IgnoreCase | RegexOptions.Singleline);
115 var match = regex.Match(s);
116
117 if (!match.Success)
118 {
119 return false;
120 }
121
122 int major, minor, patch, increment = 0;
123 string label = null;
124
125 major = int.Parse(match.Groups["major"].Value);
126 minor = int.Parse(match.Groups["minor"].Value);
127 patch = int.Parse(match.Groups["patch"].Value);
128
129 if (match.Groups["label"].Success)
130 {
131 label = match.Groups["label"].Value;
132 }
133
134 if (match.Groups["increment"].Success)
135 {
136 increment = int.Parse(match.Groups["increment"].Value);
137 }
138
139 result = new SemanticVersion(major, minor, patch, label, increment);
140
141 return true;
142 }
143
144 // Final > _(Nothing)_ > Release Candidate > Beta > Alpha > Pre
145 private static readonly Dictionary<SemanticLabel, int> LabelComparisonMap = new Dictionary<SemanticLabel, int>()
146 {
147 { SemanticLabel.Final, 6 },
148 { SemanticLabel.Unspecified, 5 },
149 { SemanticLabel.ReleaseCandidate, 4 },
150 { SemanticLabel.Beta, 3 },
151 { SemanticLabel.Alpha, 2 },
152 { SemanticLabel.Pre, 1 },
153 };
154
155 public int CompareTo(SemanticVersion other)
156 {
157 var majorComparison = major.CompareTo(other.major);
158
159 if (majorComparison != 0)
160 {
161 return majorComparison;
162 }
163
164 var minorComparison = minor.CompareTo(other.minor);
165
166 if (minorComparison != 0)
167 {
168 return minorComparison;
169 }
170
171 var patchComparison = patch.CompareTo(other.patch);
172
173 if (patchComparison != 0)
174 {
175 return patchComparison;
176 }
177
178 // Final > _(Nothing)_ > Release Candidate > Beta > Alpha > Pre
179 var ours = LabelComparisonMap[this.semanticLabel];
180 var others = LabelComparisonMap[other.semanticLabel];
181 var labelComparison = ours.CompareTo(others);
182
183 if (labelComparison != 0)
184 {
185 return labelComparison;
186 }
187
188 var incrementComparison = increment.CompareTo(other.increment);
189
190 if (incrementComparison != 0)
191 {
192 return incrementComparison;
193 }
194
195 return 0;
196 }
197
198 public override bool Equals(object obj)
199 {
200 if (!(obj is SemanticVersion))
201 {
202 return false;
203 }
204
205 var other = (SemanticVersion)obj;
206
207 return
208 other.major == major &&
209 other.minor == minor &&
210 other.patch == patch &&
211 other.semanticLabel == semanticLabel &&
212 other.increment == increment;
213 }
214
215 public override int GetHashCode()
216 {
217 return HashUtility.GetHashCode(major, minor, patch);
218 }
219
220 public static bool operator ==(SemanticVersion a, SemanticVersion b)
221 {
222 return a.Equals(b);
223 }
224
225 public static bool operator !=(SemanticVersion a, SemanticVersion b)
226 {
227 return !(a == b);
228 }
229
230 public static bool operator <(SemanticVersion a, SemanticVersion b)
231 {
232 return a.CompareTo(b) < 0;
233 }
234
235 public static bool operator >(SemanticVersion a, SemanticVersion b)
236 {
237 return a.CompareTo(b) > 0;
238 }
239
240 public static bool operator <=(SemanticVersion a, SemanticVersion b)
241 {
242 return a.CompareTo(b) <= 0;
243 }
244
245 public static bool operator >=(SemanticVersion a, SemanticVersion b)
246 {
247 return a.CompareTo(b) >= 0;
248 }
249
250 public bool IsUnset()
251 {
252 return Equals(new SemanticVersion(0, 0, 0, null, 0));
253 }
254 }
255}