summaryrefslogtreecommitdiff
path: root/src/libs/WixToolset.Versioning/WixVersion.cs
diff options
context:
space:
mode:
authorRob Mensching <rob@firegiant.com>2022-10-02 19:37:55 -0700
committerRob Mensching <rob@firegiant.com>2022-10-04 10:44:49 -0700
commit5c509f5611a45bdf9d252b88605537bd28f24a35 (patch)
treef2ac4c1509fe69f6489c56f257b59f4e346a51c7 /src/libs/WixToolset.Versioning/WixVersion.cs
parente901e04cfb8f1b759903f41b5c8f2f91e3f877aa (diff)
downloadwix-5c509f5611a45bdf9d252b88605537bd28f24a35.tar.gz
wix-5c509f5611a45bdf9d252b88605537bd28f24a35.tar.bz2
wix-5c509f5611a45bdf9d252b88605537bd28f24a35.zip
Move WixVersion to new WixToolset.Versioning package in libs segment
WixVersion is already used by the Core toolset but could also be useful for bootstrapper applications parsing bundle versions. The WixToolset.Data assembly contains a significant amount of data that bloats its size that bootstrapper applications would never need. Extracting WixVersion to its own assembly makes it much more useable. Fixes 6943
Diffstat (limited to 'src/libs/WixToolset.Versioning/WixVersion.cs')
-rw-r--r--src/libs/WixToolset.Versioning/WixVersion.cs463
1 files changed, 463 insertions, 0 deletions
diff --git a/src/libs/WixToolset.Versioning/WixVersion.cs b/src/libs/WixToolset.Versioning/WixVersion.cs
new file mode 100644
index 00000000..7333701f
--- /dev/null
+++ b/src/libs/WixToolset.Versioning/WixVersion.cs
@@ -0,0 +1,463 @@
1// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3namespace WixToolset.Versioning
4{
5 using System;
6 using System.Collections.Generic;
7
8 /// <summary>
9 /// WiX Toolset's representation of a version designed to support
10 /// a different version types including 4-part versions with very
11 /// large numbers and semantic versions with 4-part versions with
12 /// or without leading "v" indicators.
13 /// </summary>
14 public class WixVersion : IComparable, IComparable<WixVersion>, IEquatable<WixVersion>
15 {
16 /// <summary>
17 /// Gets the prefix of the version if present when parsed. Usually, 'v' or 'V'.
18 /// </summary>
19 public char? Prefix { get; set; }
20
21 /// <summary>
22 /// Gets or sets the major version.
23 /// </summary>
24 public uint Major { get; set; }
25
26 /// <summary>
27 /// Gets or sets the minor version.
28 /// </summary>
29 public uint Minor { get; set; }
30
31 /// <summary>
32 /// Gets or sets the patch version.
33 /// </summary>
34 public uint Patch { get; set; }
35
36 /// <summary>
37 /// Gets or sets the revision version.
38 /// </summary>
39 public uint Revision { get; set; }
40
41 /// <summary>
42 /// Gets or sets whether the version did not parse correctly.
43 /// </summary>
44 public bool Invalid { get; set; }
45
46 /// <summary>
47 /// Gets or sets whether the major version was defined.
48 /// </summary>
49 public bool HasMajor { get; set; }
50
51 /// <summary>
52 /// Gets or sets the whether the minor version was defined.
53 /// </summary>
54 public bool HasMinor { get; set; }
55
56 /// <summary>
57 /// Gets or sets the whether the patch version was defined.
58 /// </summary>
59 public bool HasPatch { get; set; }
60
61 /// <summary>
62 /// Gets or sets the whether the revision version was defined.
63 /// </summary>
64 public bool HasRevision { get; set; }
65
66 /// <summary>
67 /// Gets or sets the labels in the version.
68 /// </summary>
69 public WixVersionLabel[] Labels { get; set; }
70
71 /// <summary>
72 /// Gets or sets the metadata in the version.
73 /// </summary>
74 public string Metadata { get; set; }
75
76 /// <summary>
77 /// Compare to another WixVersion.
78 /// </summary>
79 /// <param name="version">WixVersion to compare.</param>
80 /// <returns>A comparison between versions.</returns>
81 public int CompareTo(WixVersion version)
82 {
83 return WixVersionComparer.Default.Compare(this, version);
84 }
85
86 /// <summary>
87 /// Compare to another object.
88 /// </summary>
89 /// <param name="version">Object to compare.</param>
90 /// <returns>A comparison between objects.</returns>
91 public int CompareTo(object version)
92 {
93 return WixVersionComparer.Default.Compare(this, version as WixVersion);
94 }
95
96 /// <summary>
97 /// Returns a value indicating whether the current System.Version object is equal to a specified object.
98 /// </summary>
99 /// <param name="version">An WixVersion to compare with the current WixVersion object, or null.</param>
100 /// <returns>
101 /// true if the current WixVersion object and obj are both WixVersion objects,
102 /// and every component of the current System.Version object matches the corresponding
103 /// component of obj; otherwise, false.
104 /// </returns>
105 public bool Equals(WixVersion version)
106 {
107 return WixVersionComparer.Default.Equals(this, version);
108 }
109
110 /// <summary>
111 /// Returns a value indicating whether the current WixVersion object is equal to a specified object.
112 /// </summary>
113 /// <param name="obj">An object to compare with the current WixVersion object, or null.</param>
114 /// <returns>
115 /// true if the current WixVersion object and obj are both WixVersion objects,
116 /// and every component of the current System.Version object matches the corresponding
117 /// component of obj; otherwise, false.
118 /// </returns>
119 public override bool Equals(object obj)
120 {
121 return WixVersionComparer.Default.Equals(this, obj as WixVersion);
122 }
123
124 /// <summary>
125 /// Returns a hash code for the current WixVersion object.
126 /// </summary>
127 /// <returns>A 32-bit signed integer hash code.</returns>
128 public override int GetHashCode()
129 {
130 return WixVersionComparer.Default.GetHashCode(this);
131 }
132
133 /// <summary>
134 /// Parse a string value into a <c>WixVersion</c>. The returned version may be invalid.
135 /// </summary>
136 /// <param name="parse">String value to parse into a version.</param>
137 /// <returns>Parsed version.</returns>
138 public static WixVersion Parse(string parse)
139 {
140 var version = new WixVersion();
141
142 var labels = new List<WixVersionLabel>();
143 var start = 0;
144 var end = parse.Length;
145
146 if (end > 0 && (parse[0] == 'v' || parse[0] == 'V'))
147 {
148 version.Prefix = parse[0];
149
150 ++start;
151 }
152
153 var partBegin = start;
154 var partEnd = start;
155 var lastPart = false;
156 var trailingDot = false;
157 var invalid = false;
158 var currentPart = 0;
159 var parsedVersionNumber = false;
160 var expectedReleaseLabels = false;
161
162 // Parse version number
163 while (start < end)
164 {
165 trailingDot = false;
166
167 // Find end of part.
168 for (; ; )
169 {
170 if (partEnd >= end)
171 {
172 lastPart = true;
173 break;
174 }
175
176 var ch = parse[partEnd];
177
178 switch (ch)
179 {
180 case '0':
181 case '1':
182 case '2':
183 case '3':
184 case '4':
185 case '5':
186 case '6':
187 case '7':
188 case '8':
189 case '9':
190 ++partEnd;
191 continue;
192 case '.':
193 trailingDot = true;
194 break;
195 case '-':
196 case '+':
197 lastPart = true;
198 break;
199 default:
200 invalid = true;
201 break;
202 }
203
204 break;
205 }
206
207 var partLength = partEnd - partBegin;
208 if (invalid || partLength <= 0)
209 {
210 invalid = true;
211 break;
212 }
213
214 // Parse version part.
215 var s = parse.Substring(partBegin, partLength);
216 if (!UInt32.TryParse(s, out var part))
217 {
218 invalid = true;
219 break;
220 }
221
222 switch (currentPart)
223 {
224 case 0:
225 version.Major = part;
226 version.HasMajor = true;
227 break;
228 case 1:
229 version.Minor = part;
230 version.HasMinor = true;
231 break;
232 case 2:
233 version.Patch = part;
234 version.HasPatch = true;
235 break;
236 case 3:
237 version.Revision = part;
238 version.HasRevision = true;
239 break;
240 }
241
242 if (trailingDot)
243 {
244 ++partEnd;
245 }
246 partBegin = partEnd;
247 ++currentPart;
248
249 if (4 <= currentPart || lastPart)
250 {
251 parsedVersionNumber = true;
252 break;
253 }
254 }
255
256 invalid |= !parsedVersionNumber || trailingDot;
257
258 if (!invalid && partBegin < end && parse[partBegin] == '-')
259 {
260 partBegin = partEnd = partBegin + 1;
261 expectedReleaseLabels = true;
262 lastPart = false;
263 }
264
265 while (expectedReleaseLabels && partBegin < end)
266 {
267 trailingDot = false;
268
269 // Find end of part.
270 for (; ; )
271 {
272 if (partEnd >= end)
273 {
274 lastPart = true;
275 break;
276 }
277
278 var ch = parse[partEnd];
279 if (ch >= '0' && ch <= '9' ||
280 ch >= 'A' && ch <= 'Z' ||
281 ch >= 'a' && ch <= 'z' ||
282 ch == '-')
283 {
284 ++partEnd;
285 continue;
286 }
287 else if (ch == '+')
288 {
289 lastPart = true;
290 }
291 else if (ch == '.')
292 {
293 trailingDot = true;
294 }
295 else
296 {
297 invalid = true;
298 }
299
300 break;
301 }
302
303 var partLength = partEnd - partBegin;
304 if (invalid || partLength <= 0)
305 {
306 invalid = true;
307 break;
308 }
309
310 WixVersionLabel label;
311 var partString = parse.Substring(partBegin, partLength);
312 if (UInt32.TryParse(partString, out var numericPart))
313 {
314 label = new WixVersionLabel(partString, numericPart);
315 }
316 else
317 {
318 label = new WixVersionLabel(partString);
319 }
320
321 labels.Add(label);
322
323 if (trailingDot)
324 {
325 ++partEnd;
326 }
327 partBegin = partEnd;
328
329 if (lastPart)
330 {
331 break;
332 }
333 }
334
335 invalid |= expectedReleaseLabels && (labels.Count == 0 || trailingDot);
336
337 if (!invalid && partBegin < end)
338 {
339 if (parse[partBegin] == '+')
340 {
341 version.Metadata = parse.Substring(partBegin + 1);
342 }
343 else
344 {
345 invalid = true;
346 }
347 }
348
349 version.Labels = labels.Count == 0 ? null : labels.ToArray();
350
351 if (invalid)
352 {
353 // If the prefix was parsed but the rest of the version was
354 // invalid, store the full invalid version in the Metadata
355 // and clear the prefix.
356 if (version.Prefix.HasValue && partBegin == 1)
357 {
358 version.Prefix = null;
359 version.Metadata = parse;
360 }
361 else // store the remaining invalid content in Metadata.
362 {
363 version.Metadata = (partBegin < end) ? parse.Substring(partBegin) : String.Empty;
364 }
365
366 version.Invalid = true;
367 }
368
369 return version;
370 }
371
372 /// <summary>
373 /// Tries to parse a string value into a valid <c>WixVersion</c>.
374 /// </summary>
375 /// <param name="parse">String value to parse into a version.</param>
376 /// <param name="version">Parsed version.</param>
377 /// <returns>True if the version was successfully parsed, or false otherwise.</returns>
378 public static bool TryParse(string parse, out WixVersion version)
379 {
380 version = WixVersion.Parse(parse);
381
382 if (version.Invalid)
383 {
384 version = null;
385 return false;
386 }
387
388 return true;
389 }
390
391 /// <summary>
392 /// Determines whether two specified WixVersion objects are equal.
393 /// </summary>
394 /// <param name="v1">The first WixVersion object.</param>
395 /// <param name="v2">The second WixVersion object.</param>
396 /// <returns>true if v1 equals v2; otherwise, false.</returns>
397 public static bool operator ==(WixVersion v1, WixVersion v2)
398 {
399 return WixVersionComparer.Default.Equals(v1, v2);
400 }
401
402 /// <summary>
403 /// Determines whether two specified System.Version objects are not equal.
404 /// </summary>
405 /// <param name="v1">The first WixVersion object.</param>
406 /// <param name="v2">The second WixVersion object.</param>
407 /// <returns>true if v1 does not equal v2; otherwise, false.</returns>
408 public static bool operator !=(WixVersion v1, WixVersion v2)
409 {
410 return !WixVersionComparer.Default.Equals(v1, v2);
411 }
412
413 /// <summary>
414 /// Determines whether the first specified System.Version object is less than the second specified System.Version object.
415 /// </summary>
416 /// <param name="v1">The first WixVersion object.</param>
417 /// <param name="v2">The second WixVersion object.</param>
418 /// <returns>true if v1 is less than v2; otherwise, false.</returns>
419 /// <exception cref="ArgumentNullException">v1 is null.</exception>
420 public static bool operator <(WixVersion v1, WixVersion v2)
421 {
422 return WixVersionComparer.Default.Compare(v1, v2) == -1;
423 }
424
425 /// <summary>
426 /// Determines whether the first specified System.Version object is greater than the second specified System.Version object.
427 /// </summary>
428 /// <param name="v1">The first WixVersion object.</param>
429 /// <param name="v2">The second WixVersion object.</param>
430 /// <returns>true if v1 is greater than v2; otherwise, false.</returns>
431 public static bool operator >(WixVersion v1, WixVersion v2)
432 {
433 return WixVersionComparer.Default.Compare(v1, v2) == 1;
434 }
435
436 /// <summary>
437 /// Determines whether the first specified System.Version object is less than or equal to the second System.Version object.
438 /// </summary>
439 /// <param name="v1">The first WixVersion object.</param>
440 /// <param name="v2">The second WixVersion object.</param>
441 /// <returns>true if v1 is less than or equal to v2; otherwise, false.</returns>
442 /// <exception cref="ArgumentNullException">v1 is null.</exception>
443 public static bool operator <=(WixVersion v1, WixVersion v2)
444 {
445 var result = WixVersionComparer.Default.Compare(v1, v2);
446
447 return result == 0 || result == -1;
448 }
449
450 /// <summary>
451 /// Determines whether the first specified System.Version object is greater than or equal to the second specified System.Version object.
452 /// </summary>
453 /// <param name="v1">The first WixVersion object.</param>
454 /// <param name="v2">The second WixVersion object.</param>
455 /// <returns>true if v1 is greater than or equal to v2; otherwise, false.</returns>
456 public static bool operator >=(WixVersion v1, WixVersion v2)
457 {
458 var result = WixVersionComparer.Default.Compare(v1, v2);
459
460 return result == 0 || result == 1;
461 }
462 }
463}