Bug 1661084 - Add CollectWhile/CollectEach algorithms for Result. r=janv

Differential Revision: https://phabricator.services.mozilla.com/D88169
This commit is contained in:
Simon Giesecke 2020-08-28 10:02:49 +00:00
Родитель a7f224accb
Коммит fccf1a2cc3
2 изменённых файлов: 166 добавлений и 0 удалений

Просмотреть файл

@ -611,6 +611,54 @@ Result<R, nsresult> ToResultGet(const Func& aFunc, Args&&... aArgs) {
return res;
}
// Like Rust's collect with a step function, not a generic iterator/range.
//
// Cond must be a function type with a return type to Result<V, E>, where
// V is convertible to bool
// - success converts to true indicates that collection shall continue
// - success converts to false indicates that collection is completed
// - error indicates that collection shall stop, propagating the error
//
// Body must a function type accepting a V xvalue with a return type convertible
// to Result<empty, E>.
template <typename Step, typename Body>
auto CollectEach(const Step& aStep, const Body& aBody)
-> Result<mozilla::Ok, typename std::result_of_t<Step()>::err_type> {
using StepResultType = typename std::result_of_t<Step()>::ok_type;
static_assert(std::is_empty_v<
typename std::result_of_t<Body(StepResultType &&)>::ok_type>);
while (true) {
StepResultType element;
MOZ_TRY_VAR(element, aStep());
if (!static_cast<bool>(element)) {
break;
}
MOZ_TRY(aBody(std::move(element)));
}
return mozilla::Ok{};
}
// Like Rust's collect with a while loop, not a generic iterator/range.
//
// Cond must be a function type accepting no parameters with a return type
// convertible to Result<bool, E>, where
// - success true indicates that collection shall continue
// - success false indicates that collection is completed
// - error indicates that collection shall stop, propagating the error
//
// Body must a function type accepting no parameters with a return type
// convertible to Result<empty, E>.
template <typename Cond, typename Body>
auto CollectWhile(const Cond& aCond, const Body& aBody)
-> Result<mozilla::Ok, typename std::result_of_t<Cond()>::err_type> {
return CollectEach(aCond, [&aBody](bool) { return aBody(); });
}
namespace dom {
namespace quota {

Просмотреть файл

@ -491,3 +491,121 @@ TEST(QuotaCommon_ToResultGet, Lambda_WithInput_Err)
EXPECT_TRUE(res.isErr());
EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE);
}
// BEGIN COPY FROM mfbt/tests/TestResult.cpp
struct Failed {
int x;
};
static GenericErrorResult<Failed&> Fail() {
static Failed failed;
return Err<Failed&>(failed);
}
static Result<Ok, Failed&> Task1(bool pass) {
if (!pass) {
return Fail(); // implicit conversion from GenericErrorResult to Result
}
return Ok();
}
// END COPY FROM mfbt/tests/TestResult.cpp
static Result<bool, Failed&> Condition(bool aNoError, bool aResult) {
return Task1(aNoError).map([aResult](auto) { return aResult; });
}
TEST(QuotaCommon_CollectWhileTest, NoFailures)
{
const size_t loopCount = 5;
size_t conditionExecutions = 0;
size_t bodyExecutions = 0;
auto result = CollectWhile(
[&conditionExecutions] {
++conditionExecutions;
return Condition(true, conditionExecutions <= loopCount);
},
[&bodyExecutions] {
++bodyExecutions;
return Task1(true);
});
static_assert(std::is_same_v<decltype(result), Result<Ok, Failed&>>);
MOZ_RELEASE_ASSERT(result.isOk());
MOZ_RELEASE_ASSERT(loopCount == bodyExecutions);
MOZ_RELEASE_ASSERT(1 + loopCount == conditionExecutions);
}
TEST(QuotaCommon_CollectWhileTest, BodyFailsImmediately)
{
size_t conditionExecutions = 0;
size_t bodyExecutions = 0;
auto result = CollectWhile(
[&conditionExecutions] {
++conditionExecutions;
return Condition(true, true);
},
[&bodyExecutions] {
++bodyExecutions;
return Task1(false);
});
static_assert(std::is_same_v<decltype(result), Result<Ok, Failed&>>);
MOZ_RELEASE_ASSERT(result.isErr());
MOZ_RELEASE_ASSERT(1 == bodyExecutions);
MOZ_RELEASE_ASSERT(1 == conditionExecutions);
}
TEST(QuotaCommon_CollectWhileTest, BodyFailsOnSecondExecution)
{
size_t conditionExecutions = 0;
size_t bodyExecutions = 0;
auto result = CollectWhile(
[&conditionExecutions] {
++conditionExecutions;
return Condition(true, true);
},
[&bodyExecutions] {
++bodyExecutions;
return Task1(bodyExecutions < 2);
});
static_assert(std::is_same_v<decltype(result), Result<Ok, Failed&>>);
MOZ_RELEASE_ASSERT(result.isErr());
MOZ_RELEASE_ASSERT(2 == bodyExecutions);
MOZ_RELEASE_ASSERT(2 == conditionExecutions);
}
TEST(QuotaCommon_CollectWhileTest, ConditionFailsImmediately)
{
size_t conditionExecutions = 0;
size_t bodyExecutions = 0;
auto result = CollectWhile(
[&conditionExecutions] {
++conditionExecutions;
return Condition(false, true);
},
[&bodyExecutions] {
++bodyExecutions;
return Task1(true);
});
static_assert(std::is_same_v<decltype(result), Result<Ok, Failed&>>);
MOZ_RELEASE_ASSERT(result.isErr());
MOZ_RELEASE_ASSERT(0 == bodyExecutions);
MOZ_RELEASE_ASSERT(1 == conditionExecutions);
}
TEST(QuotaCommon_CollectWhileTest, ConditionFailsOnSecondExecution)
{
size_t conditionExecutions = 0;
size_t bodyExecutions = 0;
auto result = CollectWhile(
[&conditionExecutions] {
++conditionExecutions;
return Condition(conditionExecutions < 2, true);
},
[&bodyExecutions] {
++bodyExecutions;
return Task1(true);
});
static_assert(std::is_same_v<decltype(result), Result<Ok, Failed&>>);
MOZ_RELEASE_ASSERT(result.isErr());
MOZ_RELEASE_ASSERT(1 == bodyExecutions);
MOZ_RELEASE_ASSERT(2 == conditionExecutions);
}