mirror of
https://github.com/NixOS/nix.git
synced 2025-11-15 23:12:44 +01:00
Merge pull request #14479 from lovesegfault/topo-sort-handle-cycles
refactor(libutil/topo-sort): return variant instead of throwing
This commit is contained in:
commit
750306234d
6 changed files with 427 additions and 77 deletions
|
|
@ -2,39 +2,61 @@
|
|||
///@file
|
||||
|
||||
#include "nix/util/error.hh"
|
||||
#include <variant>
|
||||
|
||||
namespace nix {
|
||||
|
||||
template<typename T>
|
||||
struct Cycle
|
||||
{
|
||||
T path;
|
||||
T parent;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using TopoSortResult = std::variant<std::vector<T>, Cycle<T>>;
|
||||
|
||||
template<typename T, typename Compare>
|
||||
std::vector<T> topoSort(
|
||||
std::set<T, Compare> items,
|
||||
std::function<std::set<T, Compare>(const T &)> getChildren,
|
||||
std::function<Error(const T &, const T &)> makeCycleError)
|
||||
TopoSortResult<T> topoSort(std::set<T, Compare> items, std::function<std::set<T, Compare>(const T &)> getChildren)
|
||||
{
|
||||
std::vector<T> sorted;
|
||||
decltype(items) visited, parents;
|
||||
|
||||
auto dfsVisit = [&](this auto & dfsVisit, const T & path, const T * parent) {
|
||||
if (parents.count(path))
|
||||
throw makeCycleError(path, *parent);
|
||||
std::function<std::optional<Cycle<T>>(const T & path, const T * parent)> dfsVisit;
|
||||
|
||||
if (!visited.insert(path).second)
|
||||
return;
|
||||
dfsVisit = [&](const T & path, const T * parent) -> std::optional<Cycle<T>> {
|
||||
if (parents.count(path)) {
|
||||
return Cycle{path, *parent};
|
||||
}
|
||||
|
||||
if (!visited.insert(path).second) {
|
||||
return std::nullopt;
|
||||
}
|
||||
parents.insert(path);
|
||||
|
||||
auto references = getChildren(path);
|
||||
|
||||
for (auto & i : references)
|
||||
/* Don't traverse into items that don't exist in our starting set. */
|
||||
if (i != path && items.count(i))
|
||||
dfsVisit(i, &path);
|
||||
if (i != path && items.count(i)) {
|
||||
auto result = dfsVisit(i, &path);
|
||||
if (result.has_value()) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
sorted.push_back(path);
|
||||
parents.erase(path);
|
||||
|
||||
return std::nullopt;
|
||||
};
|
||||
|
||||
for (auto & i : items)
|
||||
dfsVisit(i, nullptr);
|
||||
for (auto & i : items) {
|
||||
auto cycle = dfsVisit(i, nullptr);
|
||||
if (cycle.has_value()) {
|
||||
return *cycle;
|
||||
}
|
||||
}
|
||||
|
||||
std::reverse(sorted.begin(), sorted.end());
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue