11#include <google/protobuf/message.h>
13#include <userver/utils/assert.hpp>
14#include <userver/utils/function_ref.hpp>
15#include <userver/utils/impl/internal_tag.hpp>
16#include <userver/utils/span.hpp>
18namespace google::protobuf {
25USERVER_NAMESPACE_BEGIN
29using MessageVisitCallback = utils::function_ref<
void(google::protobuf::Message&)>;
31using FieldVisitCallback =
32 utils::function_ref<
void(google::protobuf::Message&,
const google::protobuf::FieldDescriptor&)>;
35void VisitFields(google::protobuf::Message& message, FieldVisitCallback callback);
45 google::protobuf::Message& message,
46 const google::protobuf::FieldDescriptor& field,
47 MessageVisitCallback callback
50using DescriptorList = std::vector<
const google::protobuf::Descriptor*>;
52using FieldDescriptorList = std::vector<
const google::protobuf::FieldDescriptor*>;
64const google::protobuf::FieldDescriptor*
65FindField(
const google::protobuf::Descriptor* descriptor, std::string_view field);
85 void Compile(
const google::protobuf::Descriptor* descriptor);
88 void Compile(
const DescriptorList& descriptors);
119 const Dependencies& GetFieldsWithSelectedChildren(utils::impl::InternalTag)
const {
120 return fields_with_selected_children_;
124 const Dependencies& GetReverseEdges(utils::impl::InternalTag)
const {
return reverse_edges_; }
127 const DescriptorSet& GetPropagated(utils::impl::InternalTag)
const {
return propagated_; }
130 const DescriptorSet& GetCompiled(utils::impl::InternalTag)
const {
return compiled_; }
133 explicit VisitorCompiler(
LockBehavior lock_behavior) : lock_behavior_(lock_behavior) {}
136 ~VisitorCompiler() =
default;
139 std::shared_lock<std::shared_mutex> LockRead();
142 std::unique_lock<std::shared_mutex> LockWrite();
144 const Dependencies& GetFieldsWithSelectedChildren()
const {
return fields_with_selected_children_; }
148 virtual void CompileOne(
const google::protobuf::Descriptor& descriptor) = 0;
151 virtual bool IsSelected(
const google::protobuf::Descriptor&)
const = 0;
155 DescriptorSet GetFullSubtrees(
const DescriptorList& descriptors)
const;
158 void PropagateSelected(
const google::protobuf::Descriptor* descriptor);
160 std::shared_mutex mutex_;
163 Dependencies fields_with_selected_children_;
164 Dependencies reverse_edges_;
165 DescriptorSet propagated_;
166 DescriptorSet compiled_;
171template <
typename Callback>
178 void Visit(google::protobuf::Message& message, Callback callback) {
182 std::shared_lock read_lock = LockRead();
194 constexpr int kMaxRecursionLimit = 100;
195 std::shared_lock read_lock = LockRead();
196 VisitRecursiveImpl(message, callback, kMaxRecursionLimit);
203 ~BaseVisitor() =
default;
206 virtual void DoVisit(google::protobuf::Message& message, Callback callback)
const = 0;
210 void VisitRecursiveImpl(google::protobuf::Message& message, Callback callback,
int recursion_limit) {
211 UINVARIANT(recursion_limit > 0,
"Recursion limit reached while traversing protobuf Message.");
217 const auto it = GetFieldsWithSelectedChildren().find(message.GetDescriptor());
218 if (it == GetFieldsWithSelectedChildren().end())
return;
220 const FieldDescriptorSet& fields = it->second;
221 for (
const google::protobuf::FieldDescriptor* field : fields) {
223 VisitNestedMessage(message, *field, [&](google::protobuf::Message& msg) {
224 VisitRecursiveImpl(msg, callback, recursion_limit - 1);
243class FieldsVisitor
final :
public BaseVisitor<FieldVisitCallback> {
245 using Selector = utils::function_ref<
bool(
const google::protobuf::FieldDescriptor& field)>;
265 FieldsVisitor(Selector selector,
const DescriptorList& descriptors, LockBehavior lock_behavior);
269 const Dependencies& GetSelectedFields(utils::impl::InternalTag)
const {
return selected_fields_; }
273 void CompileOne(
const google::protobuf::Descriptor& descriptor) override;
275 bool IsSelected(
const google::protobuf::Descriptor& descriptor)
const override {
276 return selected_fields_.find(&descriptor) != selected_fields_.end();
279 void DoVisit(google::protobuf::Message& message, FieldVisitCallback callback)
const override;
281 Dependencies selected_fields_;
282 const Selector selector_;
296class MessagesVisitor
final :
public BaseVisitor<MessageVisitCallback> {
298 using Selector = utils::function_ref<
bool(
const google::protobuf::Descriptor& descriptor)>;
318 MessagesVisitor(Selector selector,
const DescriptorList& descriptors, LockBehavior lock_behavior);
322 const DescriptorSet& GetSelectedMessages(utils::impl::InternalTag)
const {
return selected_messages_; }
326 void CompileOne(
const google::protobuf::Descriptor& descriptor) override;
328 bool IsSelected(
const google::protobuf::Descriptor& descriptor)
const override {
329 return selected_messages_.find(&descriptor) != selected_messages_.end();
332 void DoVisit(google::protobuf::Message& message, MessageVisitCallback callback)
const override;
334 DescriptorSet selected_messages_;
335 const Selector selector_;