diff --git a/lix/libutil/async-collect.hh b/lix/libutil/async-collect.hh index 1c65ac8d6..3cf8a3e8a 100644 --- a/lix/libutil/async-collect.hh +++ b/lix/libutil/async-collect.hh @@ -1,6 +1,8 @@ #pragma once /// @file +#include "lix/libutil/result.hh" +#include #include #include #include @@ -101,4 +103,37 @@ AsyncCollect asyncCollect(kj::Array>> promises return AsyncCollect(std::move(promises)); } +/** + * Run `fn` for every item in the `input` range asynchronously, using + * the same fail-fast semantics as `asyncCollect`. `asyncSpread` is a + * shorthand for calling `asyncCollect` with `std::tuple()` values as + * keys, awaiting that to completion, and propagating all exceptions. + */ +template +kj::Promise> asyncSpread(Input && input, Fn fn) + requires requires { + { + fn(*begin(input)) + } -> std::same_as>>; + } +{ + kj::Vector, kj::Promise>>> children; + if constexpr (requires { input.size(); }) { + children.reserve(input.size()); + } + + for (auto & i : input) { + children.add(std::tuple(), fn(i)); + } + + auto collect = asyncCollect(children.releaseAsArray()); + while (auto r = co_await collect.next()) { + if (!r->second.has_value()) { + co_return std::move(r->second); + } + } + + co_return result::success(); +} + }