Skip to content

Commit a86f830

Browse files
thedavekwonfacebook-github-bot
authored andcommitted
Introduce SyntaxGraph::StructuredNode::at
Summary: re-use type_system::FastFieldHandle Reviewed By: praihan Differential Revision: D79918154 fbshipit-source-id: 857b6f7756ac56ea57c8f4d13b861ce99538b931
1 parent a7e216e commit a86f830

File tree

2 files changed

+128
-28
lines changed

2 files changed

+128
-28
lines changed

third-party/thrift/src/thrift/lib/cpp2/schema/SyntaxGraph.h

Lines changed: 73 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,6 +630,23 @@ class StructuredNode : detail::WithDefinition, detail::WithUri {
630630
using detail::WithUri::uri;
631631
folly::span<const FieldNode> fields() const { return fields_; }
632632

633+
/**
634+
* Looks up a field by ID.
635+
*
636+
* Throws:
637+
* - std::out_of_range if the field ID is not present.
638+
*/
639+
const FieldNode& at(FieldId id) const { return at(fieldHandleFor(id)); }
640+
/**
641+
* Looks up a field by name.
642+
*
643+
* Throws:
644+
* - std::out_of_range if the field ID is not present.
645+
*/
646+
const FieldNode& at(std::string_view name) const {
647+
return at(fieldHandleFor(name));
648+
}
649+
633650
protected:
634651
StructuredNode(
635652
const detail::Resolver& resolver,
@@ -638,10 +655,65 @@ class StructuredNode : detail::WithDefinition, detail::WithUri {
638655
std::vector<FieldNode> fields)
639656
: detail::WithDefinition(resolver, definitionKey),
640657
detail::WithUri(uri),
641-
fields_(std::move(fields)) {}
658+
fields_(std::move(fields)) {
659+
std::uint16_t ordinal = 1;
660+
for (const FieldNode& field : fields_) {
661+
fieldHandleById_.emplace(
662+
field.id(), type_system::FastFieldHandle{ordinal});
663+
fieldHandleByName_.emplace(
664+
field.name(), type_system::FastFieldHandle{ordinal});
665+
++ordinal;
666+
}
667+
}
642668

643669
private:
670+
/**
671+
* Looks up a field by a fast field handle, previously obtained from
672+
* fieldHandleFor(...);
673+
*
674+
* Preconditions:
675+
* - The provided handle was obtained by calling fieldHandleFor(...) on this
676+
* instance.
677+
*
678+
* Throws:
679+
* - std::out_of_range if the field handle is invalid or out of range.
680+
*/
681+
const FieldNode& at(type_system::FastFieldHandle handle) const {
682+
if (!handle.valid() || handle.ordinal > fields_.size()) {
683+
folly::throw_exception<std::out_of_range>(
684+
fmt::format("invalid field handle: {}", handle.ordinal));
685+
}
686+
return fields_.at(handle.ordinal - 1);
687+
}
688+
689+
/**
690+
* Returns a field handle for the given field ID, if it exists, returning
691+
* `FastFieldHandle::invalid()` otherwise.
692+
*/
693+
type_system::FastFieldHandle fieldHandleFor(FieldId id) const noexcept {
694+
if (const type_system::FastFieldHandle* handle =
695+
folly::get_ptr(fieldHandleById_, id)) {
696+
return *handle;
697+
}
698+
return type_system::FastFieldHandle::invalid();
699+
}
700+
/**
701+
* Returns a field handle for the given field name, if it exists, returning
702+
* `FastFieldHandle::invalid()` otherwise.
703+
*/
704+
type_system::FastFieldHandle fieldHandleFor(
705+
std::string_view name) const noexcept {
706+
if (const type_system::FastFieldHandle* handle =
707+
folly::get_ptr(fieldHandleByName_, name)) {
708+
return *handle;
709+
}
710+
return type_system::FastFieldHandle::invalid();
711+
}
712+
644713
std::vector<FieldNode> fields_;
714+
folly::F14FastMap<FieldId, type_system::FastFieldHandle> fieldHandleById_;
715+
folly::F14FastMap<std::string_view, type_system::FastFieldHandle>
716+
fieldHandleByName_;
645717
};
646718

647719
class StructNode final : folly::MoveOnly,

third-party/thrift/src/thrift/lib/cpp2/schema/test/SyntaxGraphTest.cpp

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -185,22 +185,34 @@ TEST_F(ServiceSchemaTest, Struct) {
185185

186186
EXPECT_EQ(s.fields().size(), 2);
187187

188-
EXPECT_EQ(s.fields()[0].id(), FieldId{1});
189-
EXPECT_EQ(
190-
s.fields()[0].presence(), FieldNode::PresenceQualifier::UNQUALIFIED);
191-
EXPECT_EQ(s.fields()[0].type().asPrimitive(), Primitive::I32);
192-
EXPECT_EQ(s.fields()[0].name(), "field1");
193-
EXPECT_STREQ(s.fields()[0].name().data(), "field1");
194-
EXPECT_EQ(s.fields()[0].customDefault()->as_i32(), 10);
195-
196-
EXPECT_EQ(s.fields()[1].id(), FieldId{2});
197-
EXPECT_EQ(s.fields()[1].presence(), FieldNode::PresenceQualifier::OPTIONAL_);
198-
EXPECT_EQ(
199-
&s.fields()[1].type().asEnum(),
200-
&program.definitionsByName().at("TestEnum")->asEnum());
201-
EXPECT_EQ(s.fields()[1].name(), "field2");
202-
EXPECT_STREQ(s.fields()[1].name().data(), "field2");
203-
EXPECT_EQ(s.fields()[1].customDefault(), nullptr);
188+
auto checkField1 = [](const auto& field) {
189+
EXPECT_EQ(field.id(), FieldId{1});
190+
EXPECT_EQ(field.presence(), FieldNode::PresenceQualifier::UNQUALIFIED);
191+
EXPECT_EQ(field.type().asPrimitive(), Primitive::I32);
192+
EXPECT_EQ(field.name(), "field1");
193+
EXPECT_STREQ(field.name().data(), "field1");
194+
EXPECT_EQ(field.customDefault()->as_i32(), 10);
195+
};
196+
197+
checkField1(s.at(FieldId{1}));
198+
checkField1(s.at("field1"));
199+
200+
auto checkField2 = [&](const auto& field) {
201+
EXPECT_EQ(field.id(), FieldId{2});
202+
EXPECT_EQ(field.presence(), FieldNode::PresenceQualifier::OPTIONAL_);
203+
EXPECT_EQ(
204+
&field.type().asEnum(),
205+
&program.definitionsByName().at("TestEnum")->asEnum());
206+
EXPECT_EQ(field.name(), "field2");
207+
EXPECT_STREQ(field.name().data(), "field2");
208+
EXPECT_EQ(field.customDefault(), nullptr);
209+
};
210+
211+
checkField2(s.at(FieldId{2}));
212+
checkField2(s.at("field2"));
213+
214+
EXPECT_THROW(s.at(FieldId{3}), std::out_of_range);
215+
EXPECT_THROW(s.at("field3"), std::out_of_range);
204216

205217
EXPECT_EQ(
206218
s.toDebugString(),
@@ -229,17 +241,33 @@ TEST_F(ServiceSchemaTest, Union) {
229241

230242
EXPECT_EQ(u.fields().size(), 2);
231243

232-
EXPECT_EQ(u.fields()[0].id(), FieldId{1});
233-
EXPECT_EQ(u.fields()[0].name(), "s");
234-
EXPECT_EQ(
235-
&u.fields()[0].type().asStruct(),
236-
&program.definitionsByName().at("TestStruct")->asStruct());
237-
238-
EXPECT_EQ(u.fields()[1].id(), FieldId{2});
239-
EXPECT_EQ(u.fields()[1].name(), "e");
240-
EXPECT_EQ(
241-
&u.fields()[1].type().asEnum(),
242-
&program.definitionsByName().at("TestEnum")->asEnum());
244+
auto checkField1 = [&](const auto& field) {
245+
EXPECT_EQ(field.id(), FieldId{1});
246+
EXPECT_EQ(field.presence(), FieldNode::PresenceQualifier::UNQUALIFIED);
247+
EXPECT_EQ(
248+
&field.type().asStruct(),
249+
&program.definitionsByName().at("TestStruct")->asStruct());
250+
EXPECT_EQ(field.name(), "s");
251+
EXPECT_STREQ(field.name().data(), "s");
252+
};
253+
254+
checkField1(u.at(FieldId{1}));
255+
checkField1(u.at("s"));
256+
257+
auto checkField2 = [&](const auto& field) {
258+
EXPECT_EQ(field.id(), FieldId{2});
259+
EXPECT_EQ(field.presence(), FieldNode::PresenceQualifier::UNQUALIFIED);
260+
EXPECT_EQ(
261+
&field.type().asEnum(),
262+
&program.definitionsByName().at("TestEnum")->asEnum());
263+
EXPECT_EQ(field.name(), "e");
264+
};
265+
266+
checkField2(u.at(FieldId{2}));
267+
checkField2(u.at("e"));
268+
269+
EXPECT_THROW(u.at(FieldId{3}), std::out_of_range);
270+
EXPECT_THROW(u.at("field3"), std::out_of_range);
243271

244272
EXPECT_EQ(
245273
u.toDebugString(),

0 commit comments

Comments
 (0)