diff --git a/source/dub/dependencyresolver.d b/source/dub/dependencyresolver.d index 432399c..6b42d80 100644 --- a/source/dub/dependencyresolver.d +++ b/source/dub/dependencyresolver.d @@ -14,6 +14,7 @@ import std.array : appender, array; import std.conv : to; import std.exception : enforce; +import std.typecons : Nullable; import std.string : format, indexOf, lastIndexOf; @@ -41,7 +42,8 @@ CONFIG config; hash_t toHash() const nothrow @trusted { - size_t ret = typeid(string).getHash(&pack); + size_t ret = pack.hashOf(); + //size_t ret = typeid(string).getHash(&pack); ret ^= typeid(CONFIG).getHash(&config); return ret; } @@ -123,7 +125,7 @@ config_indices[] = 0; visited = null; - sizediff_t validateConfigs(TreeNode parent, ref string error) + sizediff_t validateConfigs(TreeNode parent, ref ConflictError error) { import std.algorithm : max; @@ -160,7 +162,7 @@ } // choose another parent config to avoid the invalid child if (parentidx > maxcpi) { - error = format("Package %s contains invalid dependency %s (no version candidates)", parent.pack, ch.pack); + error = ConflictError(ConflictError.Kind.invalidDependency, parent, ch, CONFIG.invalid); logDiagnostic("%s (ci=%s)", error, parentidx); maxcpi = parentidx; } @@ -175,13 +177,13 @@ // if we are at the root level, we can safely skip the maxcpi computation and instead choose another childidx config if (parentbase == root_base_pack) { - error = format("No match for dependency %s %s of %s", ch.pack, ch.configs, parent.pack); + error = ConflictError(ConflictError.Kind.noRootMatch, parent, ch, config); return childidx; } if (childidx > maxcpi) { maxcpi = max(childidx, parentidx); - error = format("Dependency %s -> %s %s mismatches with selected version %s", parent.pack, ch.pack, ch.configs, config); + error = ConflictError(ConflictError.Kind.childMismatch, parent, ch, config); logDebug("%s (ci=%s)", error, maxcpi); } @@ -196,7 +198,7 @@ return maxcpi; } - string first_error; + Nullable!ConflictError first_error; size_t loop_counter = 0; // Leave the possibility to opt-out from the loop limit @@ -213,9 +215,9 @@ // check if the current combination of configurations works out visited = null; - string error; + ConflictError error; auto conflict_index = validateConfigs(root, error); - if (first_error is null) first_error = error; + if (first_error.isNull) first_error = error; // print out current iteration state logDebug("Interation (ci=%s) %s", conflict_index, { @@ -249,7 +251,7 @@ else break; } if (config_indices.all!"a==0") { - if (throw_on_failure) throw new Exception("Could not find a valid dependency tree configuration: "~first_error); + if (throw_on_failure) throw new Exception(format("Could not find a valid dependency tree configuration: %s", first_error.get)); else return null; } } @@ -283,6 +285,36 @@ if (p !in required) configs.remove(p); } + + private struct ConflictError { + enum Kind { + none, + noRootMatch, + childMismatch, + invalidDependency + } + + Kind kind; + TreeNode parent; + TreeNodes child; + CONFIG config; + + string toString() + const { + final switch (kind) { + case Kind.none: return "no error"; + case Kind.noRootMatch: + return "No match for dependency %s %s of %s" + .format(child.pack, child.configs, parent.pack); + case Kind.childMismatch: + return "Dependency %s -> %s %s mismatches with selected version %s" + .format(parent.pack, child.pack, child.configs, config); + case Kind.invalidDependency: + return "Package %s contains invalid dependency %s (no version candidates)" + .format(parent.pack, child.pack); + } + } + } } enum DependencyType {