diff --git a/source/dub/dependency.d b/source/dub/dependency.d index 76a2cd5..29d1d48 100644 --- a/source/dub/dependency.d +++ b/source/dub/dependency.d @@ -35,7 +35,6 @@ */ struct Version { private { - enum MASTER_VERS = cast(size_t)(-1); enum MAX_VERS = "99999.0.0"; string m_version; } @@ -150,7 +149,16 @@ { enforce(ves.length > 0); string orig = ves; - if (ves[0] == Version.BRANCH_IDENT) { + + if (ves[0] == Version.BRANCH_IDENT && ves[1] == '>') { + // Shortcut: "~>x.y.z" variant. Last non-zero number will indicate + // the base for this so something like this: ">=x.y.z =1.0.0-beta"); + assert(!a.matches(Version("1.0.0-alpha")), "Failed: match 1.0.0-alpha with >=1.0.0-beta"); + assert(a.matches(Version("1.0.0-beta")), "Failed: match 1.0.0-beta with >=1.0.0-beta"); + assert(a.matches(Version("1.0.0")), "Failed: match 1.0.0 with >=1.0.0-beta"); + assert(a.matches(Version("1.0.0-rc")), "Failed: match 1.0.0-rc with >=1.0.0-beta"); + + // Dependency shortcut. + a = Dependency("~>0.1.2"); + b = Dependency(">=0.1.2 <0.2.0"); + assert(a == b, "Testing failed: " ~ a.to!string()); + assert(a.matches(Version("0.1.146")), "Failed: Match 0.1.146 with ~>0.1.2"); + assert(!a.matches(Version("0.2.0")), "Failed: Match 0.2.0 with ~>0.1.2"); + + a = Dependency("~>1.0.2"); + b = Dependency(">=1.0.2 <1.1.0"); + assert(a == b, "Testing failed: " ~ a.to!string()); + + a = Dependency("~>1.0.1-beta"); + b = Dependency(">=1.0.1-beta <1.0.1-betb"); + assert(a == b, "Testing failed: " ~ a.to!string()); + assert(a.matches(Version("1.0.1-beta"))); + assert(a.matches(Version("1.0.1-beta.6"))); + a = Dependency("~d2test"); assert(!a.optional); assert(a.valid); diff --git a/source/dub/semver.d b/source/dub/semver.d index b6b1607..7b7b0e9 100644 --- a/source/dub/semver.d +++ b/source/dub/semver.d @@ -9,6 +9,8 @@ import std.range; import std.string; +import std.algorithm : join, split; +import std.conv; /* General format of SemVer: a.b.c[-x.y...][+x.y...] @@ -168,9 +170,77 @@ assertLess("1.0.0-2", "1.0.0-10"); assertLess("1.0.0-99", "1.0.0-1a"); assertLess("1.0.0-99", "1.0.0-a"); + assertLess("1.0.0-alpha", "1.0.0-alphb"); + assertLess("1.0.0-alphz", "1.0.0-alphz0"); + assertLess("1.0.0-alphZ", "1.0.0-alpha"); } +/** + Given a valid semver version string, increments it in the sense of + 1.5.67 -> 1.6.0 + 1.5.67-a -> 1.5.67-b + + This helps with the "~>1.5.6" version specifier. + + The version strings must be validated using isValidVersion() before being + passed to this function. +*/ +string incrementVersion(string ver) { + // Cut off metadata, does not matter. + auto mi = ver.indexOf("+"); + if (mi > 0) ver = ver[0..mi]; + + // Check and increment pre-release numbers + mi = ver.indexOf("-"); + if (mi > 0) return ver[0..mi+1] ~ incrementDotted(ver[(mi+1)..$]); + + // Increment simple a.b.c + return incrementDotted(ver); +} + +private string incrementDotted(string ver) { + auto items = split(ver, "."); + // Find item to increment: last item before last non-zero item. + int idx = items.length-1; + while (idx >= 0 && isValidNumber(items[idx]) && to!int(items[idx]) == 0) + --idx; + + // idx is the last non-zero item, take the one before it or the first item. + idx = idx<=0 ? 0 : idx - 1; + auto to_increment = items[idx]; + if (isValidNumber(to_increment)) { + to_increment = to!string(to!int(to_increment) + 1); + } else { + // "000Z" -> "000a" + if (to_increment[$-1] == 'Z') to_increment = to_increment[0..$-1] ~ 'a'; + // "000z" -> "000z0" + else if(to_increment[$-1] == 'z') to_increment = to_increment ~ '0'; + // "000y" -> "000z" + else to_increment = to_increment[0..$-1] ~ cast(char)( cast(int)(to_increment[$-1]) + 1); + } + items[idx] = to_increment; + string incremented = join(items[0 .. idx+1], "."); + // Fill up with zeros. + for (int i=idx+1; i