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 body 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 body 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(in 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(in 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 body 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(in 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 body 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 body 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 body 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 }