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