1 /**
2  * License: <a href="http://opensource.org/licenses/MIT">MIT</a>.
3  * Authors: Dragos Carp
4  *
5  * See_Also: <a href="http://semver.org">Semantic Versioning 2.0</a>,
6  * <a href="https://github.com/isaacs/node-semver">The semantic versioner for npm</a>
7  */
8 
9 module semver;
10 
11 import std.algorithm;
12 import std.range;
13 
14 /**
15  * The version part of a version number.
16  */
17 enum VersionPart
18 {
19     /** major number */
20     MAJOR,
21     /** minor number */
22     MINOR,
23     /** patch number */
24     PATCH,
25     /** prerelease suffix */
26     PRERELEASE,
27     /** build suffix */
28     BUILD,
29 };
30 
31 /**
32  * Represent a semantic version number MAJOR[.MINOR[.PATCH]][-PRERELEASE][+BUILD].
33  */
34 struct SemVer
35 {
36     private uint[3] ids;
37     private string[] prerelease;
38     private string[] build;
39 
40     private bool _isValid;
41 
42     /**
43      * Creates and validates a version number from a string.
44      *
45      * If string format is invalid it just sets the $(D_PARAM isValid) property to $(D_KEYWORD false).
46      */
47     this(string semVer)
48     {
49         import std.array : array;
50         import std.conv : to;
51         import std.regex : matchAll, regex;
52 
53         _isValid = false;
54         if (semVer.empty)
55             return;
56         if (!semVer.skipOver('v'))
57             semVer.skipOver('=');
58 
59         auto re = regex(`^(\d+)(?:\.(\d+))?(?:\.(\d+))?(?:-([a-zA-Z\d-.]+))?(?:\+([a-zA-Z\d-.]+))?$`);
60         auto m = semVer.matchAll(re);
61         if (m.empty)
62             return;
63 
64         foreach (i, ref id; ids)
65         {
66             if (!m.captures[i+1].empty)
67                 id = m.captures[i+1].to!uint;
68         }
69 
70         if (!m.captures[4].empty)
71         {
72             prerelease = m.captures[4].splitter('.').array;
73             if (prerelease.any!empty)
74                 return;
75         }
76 
77         if (!m.captures[5].empty)
78         {
79             build = m.captures[5].splitter('.').array;
80             if (build.any!empty)
81                 return;
82         }
83 
84         _isValid = true;
85     }
86 
87     /**
88      * Return the canonical string format.
89      */
90     string toString() const
91     {
92         import std.string : format;
93 
94         if (!_isValid)
95             return "<invalid_semver>";
96 
97         string semVer = "%(%s.%)".format(ids);
98         if (!prerelease.empty)
99             semVer ~= "-" ~ "%-(%s.%)".format(prerelease);
100         if (!build.empty)
101             semVer ~= "+" ~ "%-(%s.%)".format(build);
102         return semVer;
103     }
104 
105     deprecated("Please use `isValid` instead.")
106     @property bool valid() const
107     {
108         return isValid;
109     }
110 
111     /**
112      * Property that indicates whether this $(D_PSYMBOL SemVer) is valid.
113      */
114     @property bool isValid() const
115     {
116         return _isValid;
117     }
118 
119     /**
120      * Property that indicates whether this $(D_PSYMBOL SemVer) is stable.
121      */
122     @property bool isStable() const
123     {
124         return prerelease.empty;
125     }
126 
127     deprecated("Please use `increment` instead.")
128     SemVer inc(VersionPart versionPart) const
129     {
130         return increment(versionPart);
131     }
132 
133     /**
134      * Increment version number.
135      */
136     SemVer increment(VersionPart versionPart) const
137     in
138     {
139         assert(this.isValid);
140     }
141     out(result)
142     {
143         assert(result.isValid);
144     }
145     body
146     {
147         SemVer result = "0";
148         foreach (i; VersionPart.MAJOR .. versionPart)
149             result.ids[i] = this.ids[i];
150         if (versionPart != VersionPart.PRERELEASE)
151             result.ids[versionPart] = this.ids[versionPart]+1;
152         return result;
153     }
154 
155     private SemVer appendPrerelease0()
156     {
157         if (prerelease.empty)
158             prerelease ~= "0";
159         return this;
160     }
161 
162     unittest
163     {
164         assert(SemVer("1.2.3").increment(VersionPart.MAJOR) == SemVer("2.0.0"));
165         assert(SemVer("1.2.3").increment(VersionPart.MINOR) == SemVer("1.3.0"));
166         assert(SemVer("1.2.3-alpha").increment(VersionPart.MINOR) == SemVer("1.3.0"));
167         assert(SemVer("1.2.3").increment(VersionPart.PATCH) == SemVer("1.2.4"));
168         assert(SemVer("1.2.3-alpha").increment(VersionPart.PATCH) == SemVer("1.2.4"));
169         assert(SemVer("1.2.3").increment(VersionPart.PRERELEASE) == SemVer("1.2.3"));
170         assert(SemVer("1.2.3-alpha").increment(VersionPart.PRERELEASE) == SemVer("1.2.3"));
171     }
172 
173     /**
174      * Compare this $(D_PSYMBOL SemVer) with the $(D_PARAM other) $(D_PSYMBOL SemVer).
175      *
176      * Note that the build parts are considered for this operation.
177      * Please use $(D_PSYMBOL differAt) to find whether the versions differ only on the build part.
178      */
179     int opCmp(ref const SemVer other) const
180     in
181     {
182         assert(this.isValid);
183         assert(other.isValid);
184     }
185     body
186     {
187         foreach (i; 0..ids.length)
188         {
189             if (ids[i] != other.ids[i])
190                 return ids[i] < other.ids[i] ? -1 : 1;
191         }
192 
193         int compareSufix(const string[] suffix, const string[] anotherSuffix)
194         {
195             import std.conv : to;
196             import std.string : isNumeric;
197 
198             if (!suffix.empty && anotherSuffix.empty)
199                 return -1;
200             if (suffix.empty && !anotherSuffix.empty)
201                 return 1;
202 
203             foreach (a, b; lockstep(suffix, anotherSuffix))
204             {
205                 if (a.isNumeric && b.isNumeric)
206                 {
207                     if (a.to!uint != b.to!uint)
208                         return a.to!uint < b.to!uint ? -1 : 1;
209                     else
210                         continue;
211                 }
212                 if (a != b)
213                     return a < b ? -1 : 1;
214             }
215             if (suffix.length != anotherSuffix.length)
216                 return suffix.length < anotherSuffix.length ? -1 : 1;
217             else
218                 return 0;
219         }
220 
221         auto result = compareSufix(prerelease, other.prerelease);
222         if (result != 0)
223             return result;
224         else
225             return compareSufix(build, other.build);
226     }
227 
228     /// ditto
229     int opCmp(in SemVer other) const
230     {
231         return this.opCmp(other);
232     }
233 
234     /**
235      * Check for equality between this $(D_PSYMBOL SemVer) and the $(D_PARAM other) $(D_PSYMBOL SemVer).
236      *
237      * Note that the build parts are considered for this operation.
238      * Please use $(D_PSYMBOL differAt) to find whether the versions differ only on the build part.
239      */
240     bool opEquals(ref const SemVer other) const
241     {
242         return this.opCmp(other) == 0;
243     }
244 
245     /// ditto
246     bool opEquals(in SemVer other) const
247     {
248         return this.opEquals(other);
249     }
250 
251     /**
252      * Compare two $(B different) versions and return the parte they differ on.
253      */
254     VersionPart differAt(ref const SemVer other) const
255     in
256     {
257         assert(this != other);
258     }
259     body
260     {
261         foreach (i; VersionPart.MAJOR .. VersionPart.PRERELEASE)
262         {
263             if (ids[i] != other.ids[i])
264                 return i;
265         }
266 
267         if (prerelease != other.prerelease)
268             return VersionPart.PRERELEASE;
269 
270         if (build != other.build)
271             return VersionPart.BUILD;
272 
273         assert(0, "Call 'differAt' for unequal versions only");
274     }
275 
276     /// ditto
277     VersionPart differAt(in SemVer other) const
278     {
279         return this.differAt(other);
280     }
281 }
282 
283 unittest
284 {
285     assert(!SemVer().isValid);
286     assert(!SemVer("1.2-.alpha.32").isValid);
287     assert(!SemVer("1.2-alpha+").isValid);
288     assert(!SemVer("1.2-alpha_").isValid);
289     assert(!SemVer("1.2+32.").isValid);
290     assert(!SemVer("1.2.5.6").isValid);
291     assert(!SemVer("").isValid);
292     assert(SemVer("1").isStable);
293     assert(SemVer("1.0").isStable);
294     assert(SemVer("1.0.0").isStable);
295     assert(SemVer("1.0+build3.").isStable);
296     assert(SemVer("1.0.0+build.5").isStable);
297     assert(!SemVer("1.0.0-alpha").isStable);
298     assert(!SemVer("1.0.0-alpha.1").isStable);
299 
300     assert(SemVer("1.0.0-alpha") < SemVer("1.0.0-alpha.1"));
301     assert(SemVer("1.0.0-alpha.1") < SemVer("1.0.0-alpha.beta"));
302     assert(SemVer("1.0.0-alpha.beta") < SemVer("1.0.0-beta"));
303     assert(SemVer("1.0.0-beta") < SemVer("1.0.0-beta.2"));
304     assert(SemVer("1.0.0-beta.2") < SemVer("1.0.0-beta.11"));
305     assert(SemVer("1.0.0-beta.11") < SemVer("1.0.0-rc.1"));
306     assert(SemVer("1.0.0-rc.1") < SemVer("1.0.0"));
307     assert(SemVer("1.0.0-rc.1") > SemVer("1.0.0-rc.1+build.5"));
308     assert(SemVer("1.0.0-rc.1+build.5") == SemVer("1.0.0-rc.1+build.5"));
309 
310     assert(SemVer("1.0.0").differAt(SemVer("2")) == VersionPart.MAJOR);
311     assert(SemVer("1.0.0").differAt(SemVer("1.1.1")) == VersionPart.MINOR);
312     assert(SemVer("1.0.0-rc.1").differAt(SemVer("1.0.1-rc.1")) == VersionPart.PATCH);
313     assert(SemVer("1.0.0-alpha").differAt(SemVer("1.0.0-beta")) == VersionPart.PRERELEASE);
314     assert(SemVer("1.0.0-rc.1").differAt(SemVer("1.0.0")) == VersionPart.PRERELEASE);
315     assert(SemVer("1.0.0-rc.1").differAt(SemVer("1.0.0-rc.1+build.5")) == VersionPart.BUILD);
316 }
317 
318 /**
319  * Represent a semantic version range [~|~>|^|<|<=|=|>=|>]MAJOR[.MINOR[.PATCH]].
320  */
321 struct SemVerRange
322 {
323     private struct SimpleRange
324     {
325         string op;
326         SemVer semVer;
327 
328         string toString() const
329         {
330             return op ~ semVer.toString;
331         }
332     }
333 
334     private SimpleRange[][] ranges;
335 
336     invariant()
337     {
338         assert(ranges.all!(r => r.all!(r => ["<", "<=", "=", ">=", ">"].canFind(r.op))));
339     }
340 
341     private bool _isValid;
342 
343     /**
344      * Creates and validates a semantic version range from a string.
345      *
346      * If string format is invalid it just sets the $(D_PARAM isValid) property to $(D_KEYWORD false).
347      */
348     this(string semVerRange)
349     {
350         import std.exception : enforce;
351         import std.regex : matchFirst, regex;
352         import std.string : format, strip, stripLeft;
353 
354         _isValid = false;
355         auto re = regex(`(~|~>|\^|<|<=|=|>=|>)?[v]?(\d+|\*|X|x)(?:\.(\d+|\*|X|x))?(?:\.(\d+|\*|X|x))?([\S]*)`);
356 
357         ranges = [SimpleRange[].init];
358 
359         while (!semVerRange.stripLeft.empty)
360         {
361             auto m = semVerRange.matchFirst(re);
362             if (m.empty)
363                 return;
364 
365             auto operator = m.captures[1];
366             auto wildcard = wildcardAt([m.captures[2], m.captures[3], m.captures[4]]);
367             auto expanded = expand([m.captures[2], m.captures[3], m.captures[4], m.captures[5]]);
368             if (expanded.empty)
369                 return;
370 
371             auto semVer = SemVer(expanded);
372             if (!semVer.isValid)
373                 return;
374 
375             switch (m.captures.pre.strip)
376             {
377                 case "":
378                     break;
379                 case "-":
380                     if (ranges[$-1].empty || ranges[$-1][$-1].op != "=" ||
381                         operator != "" || wildcard != VersionPart.PRERELEASE)
382                         return;
383                     ranges[$-1][$-1].op = ">=";
384                     operator = "<=";
385                     break;
386                 case "||":
387                     ranges ~= SimpleRange[].init;
388                     break;
389                 default:
390                     return;
391             }
392 
393             switch (operator)
394             {
395                 case "":
396                 case "=":
397                     final switch (wildcard)
398                     {
399                         case VersionPart.MAJOR:
400                             assert(semVer == SemVer("0.0.0"));
401                             ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
402                             break;
403                         case VersionPart.MINOR:
404                         case VersionPart.PATCH:
405                             ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
406                             ranges[$-1] ~= SimpleRange("<", semVer.increment(--wildcard).appendPrerelease0);
407                             break;
408                         case VersionPart.PRERELEASE:
409                             ranges[$-1] ~= SimpleRange("=", semVer);
410                             break;
411                         case VersionPart.BUILD:
412                             assert(0, "Unexpected build part wildcard");
413                     }
414                     break;
415                 case "<":
416                     ranges[$-1] ~= SimpleRange(operator, semVer.appendPrerelease0);
417                     break;
418                 case "<=":
419                 case ">=":
420                 case ">":
421                     if (wildcard < VersionPart.PRERELEASE)
422                         semVer.appendPrerelease0;
423                     ranges[$-1] ~= SimpleRange(operator, semVer);
424                     break;
425                 case "~":
426                     final switch (wildcard)
427                     {
428                         case VersionPart.MAJOR:
429                             return;
430                         case VersionPart.MINOR:
431                         case VersionPart.PATCH:
432                             --wildcard;
433                             break;
434                         case VersionPart.PRERELEASE:
435                             --wildcard;
436                             --wildcard;
437                             break;
438                         case VersionPart.BUILD:
439                             assert(0, "Unexpected build part wildcard");
440                     }
441                     ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
442                     ranges[$-1] ~= SimpleRange("<", semVer.increment(wildcard).appendPrerelease0);
443                     break;
444                 case "~>":
445                     final switch (wildcard)
446                     {
447                         case VersionPart.MAJOR:
448                             return;
449                         case VersionPart.MINOR:
450                             --wildcard;
451                             break;
452                         case VersionPart.PATCH:
453                         case VersionPart.PRERELEASE:
454                             --wildcard;
455                             --wildcard;
456                             break;
457                         case VersionPart.BUILD:
458                             assert(0, "Unexpected build part wildcard");
459                     }
460                     ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
461                     ranges[$-1] ~= SimpleRange("<", semVer.increment(wildcard).appendPrerelease0);
462                     break;
463                 case "^":
464                     if (wildcard == VersionPart.MAJOR || !semVer.prerelease.empty)
465                         return;
466                     if (semVer.ids[VersionPart.MAJOR] != 0)
467                     {
468                         ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
469                         ranges[$-1] ~= SimpleRange("<", semVer.increment(VersionPart.MAJOR).appendPrerelease0);
470                     }
471                     else if (semVer.ids[VersionPart.MINOR] != 0)
472                     {
473                         ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
474                         ranges[$-1] ~= SimpleRange("<", semVer.increment(VersionPart.MINOR).appendPrerelease0);
475                     } 
476                     else
477                     {
478                         ranges[$-1] ~= SimpleRange(">=", semVer.appendPrerelease0);
479                         ranges[$-1] ~= SimpleRange("<", semVer.increment(VersionPart.PATCH).appendPrerelease0);
480                     }
481                     break;
482                 default:
483                     enforce(false, "Unexpected operator %s".format(operator));
484                     break;
485             }
486             semVerRange = m.captures.post;
487         }
488         _isValid = true;
489     }
490 
491     private static VersionPart wildcardAt(string[3] semVer)
492     {
493         foreach (i; VersionPart.MAJOR..VersionPart.PRERELEASE)
494         {
495             if (["", "*", "X", "x"].canFind(semVer[i]))
496                 return i;
497         }
498         return VersionPart.PRERELEASE;
499     }
500 
501     unittest
502     {
503         assert(wildcardAt(["*", "", ""]) == VersionPart.MAJOR);
504         assert(wildcardAt(["X", "", ""]) == VersionPart.MAJOR);
505         assert(wildcardAt(["1", "", ""]) == VersionPart.MINOR);
506         assert(wildcardAt(["1", "x", ""]) == VersionPart.MINOR);
507         assert(wildcardAt(["1", "2", ""]) == VersionPart.PATCH);
508         assert(wildcardAt(["1", "2", "x"]) == VersionPart.PATCH);
509         assert(wildcardAt(["1", "2", "3"]) == VersionPart.PRERELEASE);
510     }
511 
512     private static string expand(string[4] semVer)
513     {
514         import std.string : format;
515 
516         VersionPart wildcard = wildcardAt(semVer[0..3]);
517         if (wildcard != VersionPart.PRERELEASE)
518         {
519             if (semVer[wildcard+1..$].any!`!["", "*", "X", "x"].canFind(a)`)
520                 return "";
521             foreach (j; wildcard..VersionPart.PRERELEASE)
522                 semVer[j] = "0";
523         }
524         string result = "%-(%s.%)".format(semVer[0..3]);
525         if (!semVer[3].empty)
526             result ~= semVer[3];
527         return result;
528     }
529 
530     unittest
531     {
532         assert(expand(["*", "", "", ""]) == "0.0.0");
533         assert(expand(["X", "", "", ""]) == "0.0.0");
534         assert(expand(["1", "2", "3", ""]) == "1.2.3");
535         assert(expand(["1", "2", "3", "-abc"]) == "1.2.3-abc");
536         assert(expand(["1", "2", "", ""]) == "1.2.0");
537         assert(expand(["1", "2", "", "-abc"]) == "");
538         assert(expand(["1", "2", "x", ""]) == "1.2.0");
539         assert(expand(["1", "", "", ""]) == "1.0.0");
540         assert(expand(["1", "x", "", ""]) == "1.0.0");
541     }
542 
543     /**
544      * Return expanded string representation.
545      */
546     string toString() const
547     {
548         import std.string : format;
549 
550         if (!_isValid)
551             return "<invalid_semver_range>";
552 
553         return "%(%(%s %) || %)".format(ranges);
554     }
555 
556     deprecated("Please use semver.SemVerRange.isValid instead.")
557     @property bool valid() const
558     {
559         return isValid;
560     }
561 
562     /**
563      * Property that indicates whether this $(D_PSYMBOL SemVerRange) is valid.
564      */
565     @property bool isValid() const
566     {
567         return _isValid;
568     }
569 
570     private static bool simpleRangeSatisfiedBy(SimpleRange simpleRange, SemVer semVer)
571     in
572     {
573         assert(semVer.isValid);
574         assert(["<", "<=", "=", ">=", ">"].canFind(simpleRange.op));
575         assert(simpleRange.semVer.isValid);
576     }
577     body
578     {
579         semVer.build = null;
580 
581         switch (simpleRange.op)
582         {
583             case "<":
584                 return semVer < simpleRange.semVer;
585             case "<=":
586                 return semVer <= simpleRange.semVer;
587             case "=":
588                 return semVer == simpleRange.semVer;
589             case ">=":
590                 return semVer >= simpleRange.semVer;
591             case ">":
592                 return semVer > simpleRange.semVer;
593             default:
594                 return false;
595         }
596     }
597 
598     /**
599      * Check if the $(D_PSYMBOL SemVer) $(D_PARAM semVer) satisfies this $(D_PSYMBOL SemVerRange).
600      */
601     bool satisfiedBy(SemVer semVer)
602     in
603     {
604         assert(semVer.isValid);
605         assert(isValid);
606     }
607     body
608     {
609         return ranges.any!(r => r.all!(s => simpleRangeSatisfiedBy(s, semVer)));
610     }
611 
612 }
613 
614 /**
615  * Check if the $(D_PSYMBOL SemVer) $(D_PARAM semVer) satisfies $(LREF SemVerRange) $(D_PARAM semVerRange).
616  */
617 bool satisfies(SemVer semVer, SemVerRange semVerRange)
618 {
619     return semVerRange.satisfiedBy(semVer);
620 }
621 
622 /**
623  * Return the latest $(D_PSYMBOL Semver) from $(D_PARAM semVers) array that satisfies
624  * $(D_PARAM semVerRange) $(D_PSYMBOL SemVerRange).
625  */
626 SemVer maxSatisfying(SemVer[] semVers, SemVerRange semVerRange)
627 in
628 {
629     assert(semVers.all!"a.isValid");
630     assert(semVerRange.isValid);
631 }
632 body
633 {
634     auto found = semVers.sort!"a > b".find!(a => satisfies(a, semVerRange));
635     return found.empty ? SemVer("invalid") : found[0];
636 }
637 
638 unittest
639 {
640     assert(!SemVerRange().isValid);
641     assert(SemVerRange("1.x || >=2.5.0 || 5.0.0 - 7.2.3").isValid);
642     assert(!SemVerRange("blerg").isValid);
643     assert(!SemVerRange("git+https://user:password0123@github.com/foo").isValid);
644 
645     assert(SemVer("1.2.3").satisfies(SemVerRange("1.x || >=2.5.0 || 5.0.0 - 7.2.3")));
646 
647     assert(SemVer("1.2.3").satisfies(SemVerRange("1.0.0 - 2.0.0")));
648     assert(SemVer("1.0.0").satisfies(SemVerRange("1.0.0")));
649     assert(SemVer("1.0.0+build.5").satisfies(SemVerRange("1.0.0")));
650     assert(SemVer("0.2.4").satisfies(SemVerRange(">=*")));
651     assert(SemVer("1.2.3").satisfies(SemVerRange("*")));
652     assert(SemVer("v1.2.3-foo").satisfies(SemVerRange("*")));
653     assert(SemVer("1.0.0").satisfies(SemVerRange(">=1.0.0")));
654     assert(SemVer("1.0.1").satisfies(SemVerRange(">=1.0.0")));
655     assert(SemVer("1.1.0").satisfies(SemVerRange(">=1.0.0")));
656     assert(SemVer("1.0.1").satisfies(SemVerRange(">1.0.0")));
657     assert(SemVer("1.1.0").satisfies(SemVerRange(">1.0.0")));
658     assert(SemVer("2.0.0").satisfies(SemVerRange("<=2.0.0")));
659     assert(SemVer("1.9999.9999").satisfies(SemVerRange("<=2.0.0")));
660     assert(SemVer("0.2.9").satisfies(SemVerRange("<=2.0.0")));
661     assert(SemVer("1.9999.9999").satisfies(SemVerRange("<2.0.0")));
662     assert(SemVer("0.2.9").satisfies(SemVerRange("<2.0.0")));
663     assert(SemVer("1.0.0").satisfies(SemVerRange(">=1.0.0")));
664     assert(SemVer("1.0.1").satisfies(SemVerRange(">=1.0.0")));
665     assert(SemVer("1.1.0").satisfies(SemVerRange(">=1.0.0")));
666     assert(SemVer("1.0.1").satisfies(SemVerRange(">1.0.0")));
667     assert(SemVer("1.1.0").satisfies(SemVerRange(">1.0.0")));
668     assert(SemVer("2.0.0").satisfies(SemVerRange("<=2.0.0")));
669     assert(SemVer("1.9999.9999").satisfies(SemVerRange("<=2.0.0")));
670     assert(SemVer("0.2.9").satisfies(SemVerRange("<=2.0.0")));
671     assert(SemVer("1.9999.9999").satisfies(SemVerRange("<2.0.0")));
672     assert(SemVer("0.2.9").satisfies(SemVerRange("<2.0.0")));
673     assert(SemVer("v0.1.97").satisfies(SemVerRange(">=0.1.97")));
674     assert(SemVer("0.1.97").satisfies(SemVerRange(">=0.1.97")));
675     assert(SemVer("1.2.4").satisfies(SemVerRange("0.1.20 || 1.2.4")));
676     assert(SemVer("0.0.0").satisfies(SemVerRange(">=0.2.3 || <0.0.1")));
677     assert(SemVer("0.2.3").satisfies(SemVerRange(">=0.2.3 || <0.0.1")));
678     assert(SemVer("0.2.4").satisfies(SemVerRange(">=0.2.3 || <0.0.1")));
679     assert(SemVer("2.1.3").satisfies(SemVerRange("2.x.x")));
680     assert(SemVer("1.2.3").satisfies(SemVerRange("1.2.x")));
681     assert(SemVer("2.1.3").satisfies(SemVerRange("1.2.x || 2.x")));
682     assert(SemVer("1.2.3").satisfies(SemVerRange("1.2.x || 2.x")));
683     assert(SemVer("1.2.3").satisfies(SemVerRange("x")));
684     assert(SemVer("2.1.3").satisfies(SemVerRange("2.*.*")));
685     assert(SemVer("1.2.3").satisfies(SemVerRange("1.2.*")));
686     assert(SemVer("2.1.3").satisfies(SemVerRange("1.2.* || 2.*")));
687     assert(SemVer("1.2.3").satisfies(SemVerRange("1.2.* || 2.*")));
688     assert(SemVer("1.2.3").satisfies(SemVerRange("*")));
689     assert(SemVer("2.1.2").satisfies(SemVerRange("2")));
690     assert(SemVer("2.3.1").satisfies(SemVerRange("2.3")));
691     assert(SemVer("2.4.0").satisfies(SemVerRange("~2.4")));
692     assert(SemVer("2.4.5").satisfies(SemVerRange("~2.4")));
693     assert(SemVer("3.2.2").satisfies(SemVerRange("~>3.2.1")));
694     assert(SemVer("1.2.3").satisfies(SemVerRange("~1")));
695     assert(SemVer("1.2.3").satisfies(SemVerRange("~>1")));
696     assert(SemVer("1.0.2").satisfies(SemVerRange("~1.0")));
697     assert(SemVer("1.0.12").satisfies(SemVerRange("~1.0.3")));
698     assert(SemVer("1.0.0").satisfies(SemVerRange(">=1")));
699     assert(SemVer("1.1.1").satisfies(SemVerRange("<1.2")));
700     assert(SemVer("1.1.9").satisfies(SemVerRange("<=1.2")));
701     assert(SemVer("1.0.0-bet").satisfies(SemVerRange("1")));
702     assert(SemVer("0.5.5").satisfies(SemVerRange("~v0.5.4-pre")));
703     assert(SemVer("0.5.4").satisfies(SemVerRange("~v0.5.4-pre")));
704     assert(SemVer("0.7.2").satisfies(SemVerRange("=0.7.x")));
705     assert(SemVer("0.7.2").satisfies(SemVerRange(">=0.7.x")));
706     assert(SemVer("0.7.0-asdf").satisfies(SemVerRange("=0.7.x")));
707     assert(SemVer("0.7.0-asdf").satisfies(SemVerRange(">=0.7.x")));
708     assert(SemVer("0.6.2").satisfies(SemVerRange("<=0.7.x")));
709     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 >=1.2.3")));
710     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 =1.2.3")));
711     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 1.2.3")));
712     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 >=1.2.3 1.2.3")));
713     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 1.2.3 >=1.2.3")));
714     assert(SemVer("1.2.3").satisfies(SemVerRange("~1.2.1 1.2.3")));
715     assert(SemVer("1.2.3").satisfies(SemVerRange(">=1.2.1 1.2.3")));
716     assert(SemVer("1.2.3").satisfies(SemVerRange("1.2.3 >=1.2.1")));
717     assert(SemVer("1.2.3").satisfies(SemVerRange(">=1.2.3 >=1.2.1")));
718     assert(SemVer("1.2.3").satisfies(SemVerRange(">=1.2.1 >=1.2.3")));
719     assert(SemVer("1.2.3-beta").satisfies(SemVerRange("<=1.2.3")));
720     assert(SemVer("1.3.0-beta").satisfies(SemVerRange(">1.2")));
721     assert(SemVer("1.2.8").satisfies(SemVerRange(">=1.2")));
722     assert(SemVer("1.8.1").satisfies(SemVerRange("^1.2.3")));
723     assert(SemVer("1.2.3-beta").satisfies(SemVerRange("^1.2.3")));
724     assert(SemVer("0.1.2").satisfies(SemVerRange("^0.1.2")));
725     assert(SemVer("0.1.2").satisfies(SemVerRange("^0.1")));
726     assert(SemVer("1.4.2").satisfies(SemVerRange("^1.2")));
727     assert(SemVer("1.4.2").satisfies(SemVerRange("^1.2 ^1")));
728     assert(SemVer("1.2.0-pre").satisfies(SemVerRange("^1.2")));
729     assert(SemVer("1.2.3-pre").satisfies(SemVerRange("^1.2.3")));
730 
731     assert(!SemVer("2.2.3").satisfies(SemVerRange("1.0.0 - 2.0.0")));
732     assert(!SemVer("1.0.1").satisfies(SemVerRange("1.0.0")));
733     assert(!SemVer("0.0.0").satisfies(SemVerRange(">=1.0.0")));
734     assert(!SemVer("0.0.1").satisfies(SemVerRange(">=1.0.0")));
735     assert(!SemVer("0.1.0").satisfies(SemVerRange(">=1.0.0")));
736     assert(!SemVer("0.0.1").satisfies(SemVerRange(">1.0.0")));
737     assert(!SemVer("0.1.0").satisfies(SemVerRange(">1.0.0")));
738     assert(!SemVer("3.0.0").satisfies(SemVerRange("<=2.0.0")));
739     assert(!SemVer("2.9999.9999").satisfies(SemVerRange("<=2.0.0")));
740     assert(!SemVer("2.2.9").satisfies(SemVerRange("<=2.0.0")));
741     assert(!SemVer("2.9999.9999").satisfies(SemVerRange("<2.0.0")));
742     assert(!SemVer("2.2.9").satisfies(SemVerRange("<2.0.0")));
743     assert(!SemVer("v0.1.93").satisfies(SemVerRange(">=0.1.97")));
744     assert(!SemVer("0.1.93").satisfies(SemVerRange(">=0.1.97")));
745     assert(!SemVer("1.2.3").satisfies(SemVerRange("0.1.20 || 1.2.4")));
746     assert(!SemVer("0.0.3").satisfies(SemVerRange(">=0.2.3 || <0.0.1")));
747     assert(!SemVer("0.2.2").satisfies(SemVerRange(">=0.2.3 || <0.0.1")));
748     assert(!SemVer("1.1.3").satisfies(SemVerRange("2.x.x")));
749     assert(!SemVer("3.1.3").satisfies(SemVerRange("2.x.x")));
750     assert(!SemVer("1.3.3").satisfies(SemVerRange("1.2.x")));
751     assert(!SemVer("3.1.3").satisfies(SemVerRange("1.2.x || 2.x")));
752     assert(!SemVer("1.1.3").satisfies(SemVerRange("1.2.x || 2.x")));
753     assert(!SemVer("1.1.3").satisfies(SemVerRange("2.*.*")));
754     assert(!SemVer("3.1.3").satisfies(SemVerRange("2.*.*")));
755     assert(!SemVer("1.3.3").satisfies(SemVerRange("1.2.*")));
756     assert(!SemVer("3.1.3").satisfies(SemVerRange("1.2.* || 2.*")));
757     assert(!SemVer("1.1.3").satisfies(SemVerRange("1.2.* || 2.*")));
758     assert(!SemVer("1.1.2").satisfies(SemVerRange("2")));
759     assert(!SemVer("2.4.1").satisfies(SemVerRange("2.3")));
760     assert(!SemVer("2.5.0").satisfies(SemVerRange("~2.4")));
761     assert(!SemVer("2.3.9").satisfies(SemVerRange("~2.4")));
762     assert(!SemVer("3.3.2").satisfies(SemVerRange("~>3.2.1")));
763     assert(!SemVer("3.2.0").satisfies(SemVerRange("~>3.2.1")));
764     assert(!SemVer("0.2.3").satisfies(SemVerRange("~1")));
765     assert(!SemVer("2.2.3").satisfies(SemVerRange("~>1")));
766     assert(!SemVer("1.1.0").satisfies(SemVerRange("~1.0")));
767     assert(!SemVer("1.0.0").satisfies(SemVerRange("<1")));
768     assert(!SemVer("1.1.1").satisfies(SemVerRange(">=1.2")));
769     assert(!SemVer("1.3.0").satisfies(SemVerRange("<=1.2")));
770     assert(!SemVer("2.0.0-beta").satisfies(SemVerRange("1")));
771     assert(!SemVer("0.5.4-alpha").satisfies(SemVerRange("~v0.5.4-beta")));
772     assert(!SemVer("1.0.0-beta").satisfies(SemVerRange("<1")));
773     assert(!SemVer("0.8.2").satisfies(SemVerRange("=0.7.x")));
774     assert(!SemVer("0.6.2").satisfies(SemVerRange(">=0.7.x")));
775     assert(!SemVer("0.7.2").satisfies(SemVerRange("<=0.7.x")));
776     assert(!SemVer("1.2.3-beta").satisfies(SemVerRange("<1.2.3")));
777     assert(!SemVer("1.2.3-beta").satisfies(SemVerRange("=1.2.3")));
778     assert(!SemVer("1.2.8").satisfies(SemVerRange(">1.3")));
779     assert(!SemVer("2.0.0-alpha").satisfies(SemVerRange("^1.2.3")));
780     assert(!SemVer("1.2.2").satisfies(SemVerRange("^1.2.3")));
781     assert(!SemVer("1.1.9").satisfies(SemVerRange("^1.2")));
782     assert(!SemVer("2.0.0-pre").satisfies(SemVerRange("^1.2.3")));
783 
784     auto semVers = [SemVer("1.1.0"), SemVer("1.0.0"), SemVer("0.8.0")];
785     assert(semVers.maxSatisfying(SemVerRange("<=1.0.0")) == SemVer("1.0.0"));
786     assert(semVers.maxSatisfying(SemVerRange(">=1.0")) == SemVer("1.1.0"));
787 
788     semVers = [SemVer("1.0.0+build.3"), SemVer("1.0.0+build.1"), SemVer("1.1.0")];
789     assert(semVers.maxSatisfying(SemVerRange("<=1.0.0")) == SemVer("1.0.0+build.3"));
790     assert(semVers.maxSatisfying(SemVerRange(">=1.0")) == SemVer("1.1.0"));
791 }