Skip to content

Commit

Permalink
map matcher: removing exceptions
Browse files Browse the repository at this point in the history
Signed-off-by: Alyssa Wilk <[email protected]>
  • Loading branch information
alyssawilk committed Oct 9, 2024
1 parent c6761de commit f4658a5
Show file tree
Hide file tree
Showing 8 changed files with 103 additions and 73 deletions.
17 changes: 14 additions & 3 deletions source/common/matcher/exact_map_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,27 @@ namespace Matcher {
*/
template <class DataType> class ExactMapMatcher : public MapMatcher<DataType> {
public:
ExactMapMatcher(DataInputPtr<DataType>&& data_input,
absl::optional<OnMatch<DataType>> on_no_match)
: MapMatcher<DataType>(std::move(data_input), std::move(on_no_match)) {}
static absl::StatusOr<std::unique_ptr<ExactMapMatcher>>
create(DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match) {
absl::Status creation_status = absl::OkStatus();
auto ret = std::unique_ptr<ExactMapMatcher<DataType>>(
new ExactMapMatcher<DataType>(std::move(data_input), on_no_match, creation_status));
RETURN_IF_NOT_OK_REF(creation_status);
return ret;
}

void addChild(std::string value, OnMatch<DataType>&& on_match) override {
const auto itr_and_exists = children_.emplace(value, std::move(on_match));
ASSERT(itr_and_exists.second);
}

protected:
template <class DataType2, class ActionFactoryContext> friend class MatchTreeFactory;

ExactMapMatcher(DataInputPtr<DataType>&& data_input,
absl::optional<OnMatch<DataType>> on_no_match, absl::Status& creation_status)
: MapMatcher<DataType>(std::move(data_input), std::move(on_no_match), creation_status) {}

absl::optional<OnMatch<DataType>> doMatch(const std::string& data) override {
const auto itr = children_.find(data);
if (itr != children_.end()) {
Expand Down
22 changes: 12 additions & 10 deletions source/common/matcher/map_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ namespace Matcher {
template <class DataType>
class MapMatcher : public MatchTree<DataType>, Logger::Loggable<Logger::Id::matcher> {
public:
MapMatcher(DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match)
: data_input_(std::move(data_input)), on_no_match_(std::move(on_no_match)) {
auto input_type = data_input_->dataInputType();
if (input_type != DefaultMatchingDataType) {
throwEnvoyExceptionOrPanic(
absl::StrCat("Unsupported data input type: ", input_type,
", currently only string type is supported in map matcher"));
}
}

// Adds a child to the map.
virtual void addChild(std::string value, OnMatch<DataType>&& on_match) PURE;

Expand Down Expand Up @@ -59,6 +49,18 @@ class MapMatcher : public MatchTree<DataType>, Logger::Loggable<Logger::Id::matc
}

protected:
template <class DataType2, class ActionFactoryContext> friend class MatchTreeFactory;
MapMatcher(DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match,
absl::Status& creation_status)
: data_input_(std::move(data_input)), on_no_match_(std::move(on_no_match)) {
auto input_type = data_input_->dataInputType();
if (input_type != DefaultMatchingDataType) {
creation_status = absl::InvalidArgumentError(
absl::StrCat("Unsupported data input type: ", input_type,
", currently only string type is supported in map matcher"));
}
}

const DataInputPtr<DataType> data_input_;
const absl::optional<OnMatch<DataType>> on_no_match_;

Expand Down
17 changes: 12 additions & 5 deletions source/common/matcher/matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -273,11 +273,12 @@ class MatchTreeFactory : public OnMatchFactory<DataType> {
switch (matcher.matcher_tree().tree_type_case()) {
case MatcherType::MatcherTree::kExactMatchMap: {
return createMapMatcher<ExactMapMatcher>(matcher.matcher_tree().exact_match_map(), data_input,
on_no_match);
on_no_match, &ExactMapMatcher<DataType>::create);
}
case MatcherType::MatcherTree::kPrefixMatchMap: {
return createMapMatcher<PrefixMapMatcher>(matcher.matcher_tree().prefix_match_map(),
data_input, on_no_match);
data_input, on_no_match,
&PrefixMapMatcher<DataType>::create);
}
case MatcherType::MatcherTree::TREE_TYPE_NOT_SET:
PANIC("unexpected matcher type");
Expand All @@ -294,10 +295,14 @@ class MatchTreeFactory : public OnMatchFactory<DataType> {
PANIC_DUE_TO_CORRUPT_ENUM;
}

using MapCreationFunction = std::function<absl::StatusOr<std::unique_ptr<MapMatcher<DataType>>>(
DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match)>;

template <template <class> class MapMatcherType, class MapType>
MatchTreeFactoryCb<DataType>
createMapMatcher(const MapType& map, DataInputFactoryCb<DataType> data_input,
absl::optional<OnMatchFactoryCb<DataType>>& on_no_match) {
absl::optional<OnMatchFactoryCb<DataType>>& on_no_match,
MapCreationFunction creation_function) {
std::vector<std::pair<std::string, OnMatchFactoryCb<DataType>>> match_children;
match_children.reserve(map.map().size());

Expand All @@ -306,9 +311,11 @@ class MatchTreeFactory : public OnMatchFactory<DataType> {
std::make_pair(children.first, *MatchTreeFactory::createOnMatch(children.second)));
}

return [match_children, data_input, on_no_match]() {
auto multimap_matcher = std::make_unique<MapMatcherType<DataType>>(
return [match_children, data_input, on_no_match, creation_function]() {
auto matcher_or_error = creation_function(
data_input(), on_no_match ? absl::make_optional((*on_no_match)()) : absl::nullopt);
THROW_IF_NOT_OK(matcher_or_error.status());
auto multimap_matcher = std::move(*matcher_or_error);
for (const auto& children : match_children) {
multimap_matcher->addChild(children.first, children.second());
}
Expand Down
15 changes: 12 additions & 3 deletions source/common/matcher/prefix_map_matcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,24 @@ namespace Matcher {
*/
template <class DataType> class PrefixMapMatcher : public MapMatcher<DataType> {
public:
PrefixMapMatcher(DataInputPtr<DataType>&& data_input,
absl::optional<OnMatch<DataType>> on_no_match)
: MapMatcher<DataType>(std::move(data_input), std::move(on_no_match)) {}
static absl::StatusOr<std::unique_ptr<PrefixMapMatcher>>
create(DataInputPtr<DataType>&& data_input, absl::optional<OnMatch<DataType>> on_no_match) {
absl::Status creation_status = absl::OkStatus();
auto ret = std::unique_ptr<PrefixMapMatcher<DataType>>(
new PrefixMapMatcher<DataType>(std::move(data_input), on_no_match, creation_status));
RETURN_IF_NOT_OK_REF(creation_status);
return ret;
}

void addChild(std::string value, OnMatch<DataType>&& on_match) override {
children_.add(value, std::make_shared<OnMatch<DataType>>(std::move(on_match)));
}

protected:
PrefixMapMatcher(DataInputPtr<DataType>&& data_input,
absl::optional<OnMatch<DataType>> on_no_match, absl::Status& creation_status)
: MapMatcher<DataType>(std::move(data_input), std::move(on_no_match), creation_status) {}

absl::optional<OnMatch<DataType>> doMatch(const std::string& data) override {
const auto result = children_.findLongestPrefix(data);
if (result) {
Expand Down
41 changes: 21 additions & 20 deletions test/common/matcher/exact_map_matcher_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,86 +12,87 @@ namespace Envoy {
namespace Matcher {

TEST(ExactMapMatcherTest, NoMatch) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "blah"}),
absl::nullopt);

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNoMatch(result);
}

TEST(ExactMapMatcherTest, NoMatchDueToNoData) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::AllDataAvailable, absl::monostate()}),
absl::nullopt);

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNoMatch(result);
}

TEST(ExactMapMatcherTest, NoMatchWithFallback) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "blah"}),
stringOnMatch<TestData>("no_match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "no_match");
}

TEST(ExactMapMatcherTest, Match) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "match");
}

TEST(ExactMapMatcherTest, DataNotAvailable) {
ExactMapMatcher<TestData> matcher(std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::NotAvailable, {}}),
stringOnMatch<TestData>("no_match"));
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::NotAvailable, {}}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNotEnoughDataForMatch(result);
}

TEST(ExactMapMatcherTest, MoreDataMightBeAvailableNoMatch) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::MoreDataMightBeAvailable, "no match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNotEnoughDataForMatch(result);
}

TEST(ExactMapMatcherTest, MoreDataMightBeAvailableMatch) {
ExactMapMatcher<TestData> matcher(
std::unique_ptr<ExactMapMatcher<TestData>> matcher = *ExactMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::MoreDataMightBeAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "match");
}
} // namespace Matcher
Expand Down
57 changes: 29 additions & 28 deletions test/common/matcher/prefix_map_matcher_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,114 +12,115 @@ namespace Envoy {
namespace Matcher {

TEST(PrefixMapMatcherTest, NoMatch) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
absl::nullopt);

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNoMatch(result);
}

TEST(PrefixMapMatcherTest, NoMatchDueToNoData) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::AllDataAvailable, absl::monostate()}),
absl::nullopt);

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNoMatch(result);
}

TEST(PrefixMapMatcherTest, NoMatchWithFallback) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "no_match");
}

TEST(PrefixMapMatcherTest, Match) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "match");
}

TEST(PrefixMapMatcherTest, PrefixMatch) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("mat", stringOnMatch<TestData>("mat"));
matcher->addChild("mat", stringOnMatch<TestData>("mat"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "mat");
}

TEST(PrefixMapMatcherTest, LongestPrefixMatch) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::AllDataAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("mat", stringOnMatch<TestData>("mat"));
matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher.addChild("matcher", stringOnMatch<TestData>("matcher"));
matcher->addChild("mat", stringOnMatch<TestData>("mat"));
matcher->addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("matcher", stringOnMatch<TestData>("matcher"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "match");
}

TEST(PrefixMapMatcherTest, DataNotAvailable) {
PrefixMapMatcher<TestData> matcher(std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::NotAvailable, {}}),
stringOnMatch<TestData>("no_match"));
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(
DataInputGetResult{DataInputGetResult::DataAvailability::NotAvailable, {}}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNotEnoughDataForMatch(result);
}

TEST(PrefixMapMatcherTest, MoreDataMightBeAvailableNoMatch) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::MoreDataMightBeAvailable, "no match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyNotEnoughDataForMatch(result);
}

TEST(PrefixMapMatcherTest, MoreDataMightBeAvailableMatch) {
PrefixMapMatcher<TestData> matcher(
std::unique_ptr<PrefixMapMatcher<TestData>> matcher = *PrefixMapMatcher<TestData>::create(
std::make_unique<TestInput>(DataInputGetResult{
DataInputGetResult::DataAvailability::MoreDataMightBeAvailable, "match"}),
stringOnMatch<TestData>("no_match"));

matcher.addChild("match", stringOnMatch<TestData>("match"));
matcher->addChild("match", stringOnMatch<TestData>("match"));

TestData data;
const auto result = matcher.match(data);
const auto result = matcher->match(data);
verifyImmediateMatch(result, "match");
}

Expand Down
Loading

0 comments on commit f4658a5

Please sign in to comment.