Skip to content

Commit

Permalink
extended path visitor: structure
Browse files Browse the repository at this point in the history
Summary: implement structure

Differential Revision:
D65784836

Privacy Context Container: L1125642

fbshipit-source-id: 256aeb4208c3a4e9e36b9b58358e16063374fd76
  • Loading branch information
Wei-Cheng Lin authored and facebook-github-bot committed Nov 16, 2024
1 parent 2e4b4a0 commit 5cef685
Show file tree
Hide file tree
Showing 4 changed files with 162 additions and 4 deletions.
2 changes: 2 additions & 0 deletions fboss/thrift_cow/nodes/tests/test.thrift
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ union TestUnion {

struct ChildStruct {
1: map<i32, bool> childMap;
2: map<string, i32> strMap;
3: map<string, switch_config.L4PortRange> structMap;
}

struct TestStruct {
Expand Down
64 changes: 62 additions & 2 deletions fboss/thrift_cow/visitors/ExtendedPathVisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,36 @@ struct ExtendedPathVisitor<apache::thrift::type_class::structure> {
Func&& f)
requires(!is_cow_type_v<Node> && !is_field_type_v<Node>)
{
throw std::runtime_error("Sructure: not implemented yet");
using Members = typename apache::thrift::reflect_struct<Node>::members;

const auto& elem = *begin++;
auto raw = elem.raw_ref();
if (!raw) {
// Error! wildcards not supported for enum or struct
return;
}

// Perform trie search over all members for key
visitMember<Members>(*raw, [&](auto indexed) {
using member = decltype(fatal::tag_type(indexed));
using name = typename member::name;
using tc = typename member::type_class;
typename member::getter getter;

// Recurse further
auto& child = getter(node);

std::string memberName = options.outputIdPaths
? folly::to<std::string>(member::id::value)
: std::string(fatal::z_data<name>(), fatal::size<name>::value);

path.push_back(std::move(memberName));

ExtendedPathVisitor<tc>::visit(
path, child, begin, end, options, std::forward<Func>(f));

path.pop_back();
});
}

template <typename Node, typename Func>
Expand All @@ -536,7 +565,38 @@ struct ExtendedPathVisitor<apache::thrift::type_class::structure> {
Func&& f)
requires(std::is_same_v<typename Node::CowType, HybridNodeType>)
{
throw std::runtime_error("Sructure: not implemented yet");
auto& tObj = node.ref();
using T = typename Node::ThriftType;
using Members = typename apache::thrift::reflect_struct<T>::members;

const auto& elem = *begin++;
auto raw = elem.raw_ref();
if (!raw) {
// Error! wildcards not supported for enum or struct
return;
}

// Perform trie search over all members for key
visitMember<Members>(*raw, [&](auto indexed) {
using member = decltype(fatal::tag_type(indexed));
using name = typename member::name;
using tc = typename member::type_class;
typename member::getter getter;

// Recurse further
auto& child = getter(tObj);

std::string memberName = options.outputIdPaths
? folly::to<std::string>(member::id::value)
: std::string(fatal::z_data<name>(), fatal::size<name>::value);

path.push_back(std::move(memberName));

ExtendedPathVisitor<tc>::visit(
path, child, begin, end, options, std::forward<Func>(f));

path.pop_back();
});
}

template <typename Fields, typename Func>
Expand Down
94 changes: 93 additions & 1 deletion fboss/thrift_cow/visitors/tests/ExtendedPathVisitorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ TestStruct createTestStruct() {
"mapOfStringToI32", dynamic::object)(
"listOfPrimitives", dynamic::array())("setOfI32", dynamic::array())(
"mapOfI32ToListOfStructs", dynamic::object())(
"hybridMapOfMap", dynamic::object());
"hybridMapOfMap", dynamic::object())(
"hybridStruct",
dynamic::object("strMap", dynamic::object())(
"structMap", dynamic::object()));

for (int i = 0; i <= 20; ++i) {
testDyn["mapOfStringToI32"][fmt::format("test{}", i)] = i;
Expand All @@ -47,6 +50,9 @@ TestStruct createTestStruct() {
dynamic innerMap = dynamic::object();
innerMap[i * 10] = i * 100;
testDyn["hybridMapOfMap"][i] = std::move(innerMap);
testDyn["hybridStruct"]["strMap"][fmt::format("test{}", i)] = i;
testDyn["hybridStruct"]["structMap"][fmt::format("test{}", i)] =
dynamic::object("min", i)("max", i + 10);
}

return facebook::thrift::from_dynamic<TestStruct>(
Expand Down Expand Up @@ -449,3 +455,89 @@ TEST(ExtendedPathVisitorTests, AccessAnySet) {
*nodeA, path.path()->begin(), path.path()->end(), options, processPath);
EXPECT_THAT(visited, ::testing::ContainerEq(expected));
}

TYPED_TEST(ExtendedPathVisitorTests, HybridStructAccess) {
auto structA = createTestStruct();
auto nodeA = this->initNode(structA);

std::set<std::pair<std::vector<std::string>, folly::dynamic>> visited;
auto processPath = [&visited](auto&& path, auto&& node) {
if constexpr (is_cow_type_v<decltype(node)>) {
visited.emplace(std::make_pair(path, node.toFollyDynamic()));
} else {
folly::dynamic out;
facebook::thrift::to_dynamic(
out, node, facebook::thrift::dynamic_format::JSON_1);
visited.emplace(std::make_pair(path, out));
}
};
// hybridStruct/strMap/.*
{
auto path = ext_path_builder::raw("hybridStruct").raw("strMap").any().get();
std::set<std::pair<std::vector<std::string>, folly::dynamic>> expected = {
{{"hybridStruct", "strMap", "test0"}, 0},
{{"hybridStruct", "strMap", "test1"}, 1},
{{"hybridStruct", "strMap", "test2"}, 2},
{{"hybridStruct", "strMap", "test3"}, 3},
{{"hybridStruct", "strMap", "test4"}, 4},
{{"hybridStruct", "strMap", "test5"}, 5},
{{"hybridStruct", "strMap", "test6"}, 6},
{{"hybridStruct", "strMap", "test7"}, 7},
{{"hybridStruct", "strMap", "test8"}, 8},
{{"hybridStruct", "strMap", "test9"}, 9},
{{"hybridStruct", "strMap", "test10"}, 10},
{{"hybridStruct", "strMap", "test11"}, 11},
{{"hybridStruct", "strMap", "test12"}, 12},
{{"hybridStruct", "strMap", "test13"}, 13},
{{"hybridStruct", "strMap", "test14"}, 14},
{{"hybridStruct", "strMap", "test15"}, 15},
{{"hybridStruct", "strMap", "test16"}, 16},
{{"hybridStruct", "strMap", "test17"}, 17},
{{"hybridStruct", "strMap", "test18"}, 18},
{{"hybridStruct", "strMap", "test19"}, 19},
{{"hybridStruct", "strMap", "test20"}, 20},
};

ExtPathVisitorOptions options;
RootExtendedPathVisitor::visit(
*nodeA, path.path()->begin(), path.path()->end(), options, processPath);
EXPECT_THAT(visited, ::testing::ContainerEq(expected));
}
// hybridStruct/strMap/.*
{
auto path = ext_path_builder::raw("hybridStruct")
.raw("structMap")
.any()
.raw("min")
.get();
std::set<std::pair<std::vector<std::string>, folly::dynamic>> expected = {
{{"hybridStruct", "structMap", "test0", "min"}, 0},
{{"hybridStruct", "structMap", "test1", "min"}, 1},
{{"hybridStruct", "structMap", "test2", "min"}, 2},
{{"hybridStruct", "structMap", "test3", "min"}, 3},
{{"hybridStruct", "structMap", "test4", "min"}, 4},
{{"hybridStruct", "structMap", "test5", "min"}, 5},
{{"hybridStruct", "structMap", "test6", "min"}, 6},
{{"hybridStruct", "structMap", "test7", "min"}, 7},
{{"hybridStruct", "structMap", "test8", "min"}, 8},
{{"hybridStruct", "structMap", "test9", "min"}, 9},
{{"hybridStruct", "structMap", "test10", "min"}, 10},
{{"hybridStruct", "structMap", "test11", "min"}, 11},
{{"hybridStruct", "structMap", "test12", "min"}, 12},
{{"hybridStruct", "structMap", "test13", "min"}, 13},
{{"hybridStruct", "structMap", "test14", "min"}, 14},
{{"hybridStruct", "structMap", "test15", "min"}, 15},
{{"hybridStruct", "structMap", "test16", "min"}, 16},
{{"hybridStruct", "structMap", "test17", "min"}, 17},
{{"hybridStruct", "structMap", "test18", "min"}, 18},
{{"hybridStruct", "structMap", "test19", "min"}, 19},
{{"hybridStruct", "structMap", "test20", "min"}, 20},
};

ExtPathVisitorOptions options;
visited.clear();
RootExtendedPathVisitor::visit(
*nodeA, path.path()->begin(), path.path()->end(), options, processPath);
EXPECT_THAT(visited, ::testing::ContainerEq(expected));
}
}
6 changes: 5 additions & 1 deletion fboss/thrift_cow/visitors/tests/RecurseVisitorTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ folly::dynamic createTestDynamic() {
"mapB", dynamic::object())("cowMap", dynamic::object())(
"hybridMap", dynamic::object())("hybridList", dynamic::array())(
"hybridSet", dynamic::array())("hybridUnion", dynamic::object())(
"hybridStruct", dynamic::object("childMap", dynamic::object()))(
"hybridStruct",
dynamic::object("childMap", dynamic::object())(
"strMap", dynamic::object())("structMap", dynamic::object()))(
"hybridMapOfI32ToStruct", dynamic::object())(
"hybridMapOfMap", dynamic::object());
}
Expand Down Expand Up @@ -142,6 +144,8 @@ TYPED_TEST(RecurseVisitorTests, TestFullRecurse) {

std::map<std::vector<std::string>, folly::dynamic> hybridDeepLeaves = {
{{"hybridStruct", "childMap"}, testDyn["hybridStruct"]["childMap"]},
{{"hybridStruct", "strMap"}, testDyn["hybridStruct"]["strMap"]},
{{"hybridStruct", "structMap"}, testDyn["hybridStruct"]["structMap"]},
{{"mapOfEnumToStruct"}, testDyn["mapOfEnumToStruct"]},
{{"mapOfEnumToStruct", "3"}, testDyn["mapOfEnumToStruct"][3]},
{{"mapOfEnumToStruct", "3", "min"},
Expand Down

0 comments on commit 5cef685

Please sign in to comment.