| ... | ... | @@ -490,11 +490,26 @@ pub const Version = struct { |
| 490 | 490 | } |
| 491 | 491 | |
| 492 | 492 | pub fn parse(text: []const u8) !Version { |
| 493 | | var it = std.mem.split(text, "."); |
| 493 | var end: usize = 0; |
| 494 | while (end < text.len) : (end += 1) { |
| 495 | const c = text[end]; |
| 496 | if (!std.ascii.isDigit(c) and c != '.') break; |
| 497 | } |
| 498 | // found no digits or '.' before unexpected character |
| 499 | if (end == 0) return error.InvalidVersion; |
| 500 | |
| 501 | var it = std.mem.split(text[0..end], "."); |
| 502 | // substring is not empty, first call will succeed |
| 503 | const major = it.next().?; |
| 504 | if (major.len == 0) return error.InvalidVersion; |
| 505 | const minor = it.next() orelse "0"; |
| 506 | // ignore 'patch' if 'minor' is invalid |
| 507 | const patch = if (minor.len == 0) "0" else (it.next() orelse "0"); |
| 508 | |
| 494 | 509 | return Version{ |
| 495 | | .major = try std.fmt.parseInt(u32, it.next() orelse return error.InvalidVersion, 10), |
| 496 | | .minor = try std.fmt.parseInt(u32, it.next() orelse "0", 10), |
| 497 | | .patch = try std.fmt.parseInt(u32, it.next() orelse "0", 10), |
| 510 | .major = try std.fmt.parseUnsigned(u32, major, 10), |
| 511 | .minor = try std.fmt.parseUnsigned(u32, if (minor.len == 0) "0" else minor, 10), |
| 512 | .patch = try std.fmt.parseUnsigned(u32, if (patch.len == 0) "0" else patch, 10), |
| 498 | 513 | }; |
| 499 | 514 | } |
| 500 | 515 | |
| ... | ... | @@ -520,6 +535,56 @@ pub const Version = struct { |
| 520 | 535 | } |
| 521 | 536 | }; |
| 522 | 537 | |
| 538 | test "Version.parse" { |
| 539 | @setEvalBranchQuota(3000); |
| 540 | try testVersionParse(); |
| 541 | comptime (try testVersionParse()); |
| 542 | } |
| 543 | |
| 544 | pub fn testVersionParse() !void { |
| 545 | const f = struct { |
| 546 | fn eql(text: []const u8, v1: u32, v2: u32, v3: u32) !void { |
| 547 | const v = try Version.parse(text); |
| 548 | std.testing.expect(v.major == v1 and v.minor == v2 and v.patch == v3); |
| 549 | } |
| 550 | |
| 551 | fn err(text: []const u8, expected_err: anyerror) !void { |
| 552 | _ = Version.parse(text) catch |actual_err| { |
| 553 | if (actual_err == expected_err) return; |
| 554 | return actual_err; |
| 555 | }; |
| 556 | return error.Unreachable; |
| 557 | } |
| 558 | }; |
| 559 | |
| 560 | try f.eql("2.6.32.11-svn21605", 2, 6, 32); // Debian PPC |
| 561 | try f.eql("2.11.2(0.329/5/3)", 2, 11, 2); // MinGW |
| 562 | try f.eql("5.4.0-1018-raspi", 5, 4, 0); // Ubuntu |
| 563 | try f.eql("5.7.12_3", 5, 7, 12); // Void |
| 564 | try f.eql("2.13-DEVELOPMENT", 2, 13, 0); // DragonFly |
| 565 | try f.eql("2.3-35", 2, 3, 0); |
| 566 | try f.eql("1a.4", 1, 0, 0); |
| 567 | try f.eql("3.b1.0", 3, 0, 0); |
| 568 | try f.eql("1.4beta", 1, 4, 0); |
| 569 | try f.eql("2.7.pre", 2, 7, 0); |
| 570 | try f.eql("0..3", 0, 0, 0); |
| 571 | try f.eql("8.008.", 8, 8, 0); |
| 572 | try f.eql("01...", 1, 0, 0); |
| 573 | try f.eql("55", 55, 0, 0); |
| 574 | try f.eql("4294967295.0.1", 4294967295, 0, 1); |
| 575 | try f.eql("429496729_6", 429496729, 0, 0); |
| 576 | |
| 577 | try f.err("foobar", error.InvalidVersion); |
| 578 | try f.err("", error.InvalidVersion); |
| 579 | try f.err("-1", error.InvalidVersion); |
| 580 | try f.err("+4", error.InvalidVersion); |
| 581 | try f.err(".", error.InvalidVersion); |
| 582 | try f.err("....3", error.InvalidVersion); |
| 583 | try f.err("4294967296", error.Overflow); |
| 584 | try f.err("5000877755", error.Overflow); |
| 585 | // error.InvalidCharacter is not possible anymore |
| 586 | } |
| 587 | |
| 523 | 588 | /// This data structure is used by the Zig language code generation and |
| 524 | 589 | /// therefore must be kept in sync with the compiler implementation. |
| 525 | 590 | pub const CallOptions = struct { |