• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #include <string>
9 
10 #include "absl/log/absl_check.h"
11 #include "google/protobuf/compiler/cpp/helpers.h"
12 #include "google/protobuf/compiler/rust/accessors/accessor_case.h"
13 #include "google/protobuf/compiler/rust/accessors/generator.h"
14 #include "google/protobuf/compiler/rust/context.h"
15 #include "google/protobuf/compiler/rust/naming.h"
16 #include "google/protobuf/compiler/rust/upb_helpers.h"
17 #include "google/protobuf/descriptor.h"
18 #include "google/protobuf/descriptor.pb.h"
19 
20 namespace google {
21 namespace protobuf {
22 namespace compiler {
23 namespace rust {
24 namespace {
25 
MapElementTypeName(const FieldDescriptor & field)26 std::string MapElementTypeName(const FieldDescriptor& field) {
27   auto cpp_type = field.cpp_type();
28   switch (cpp_type) {
29     case FieldDescriptor::CPPTYPE_MESSAGE:
30       return cpp::QualifiedClassName(field.message_type());
31     case FieldDescriptor::CPPTYPE_ENUM:
32       return cpp::QualifiedClassName(field.enum_type());
33     default:
34       return cpp::PrimitiveTypeName(cpp_type);
35   }
36 }
37 
38 }  // namespace
39 
InMsgImpl(Context & ctx,const FieldDescriptor & field,AccessorCase accessor_case) const40 void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field,
41                     AccessorCase accessor_case) const {
42   auto& key_type = *field.message_type()->map_key();
43   auto& value_type = *field.message_type()->map_value();
44   std::string field_name = FieldNameWithCollisionAvoidance(field);
45 
46   ctx.Emit(
47       {{"field", RsSafeName(field_name)},
48        {"raw_field_name", field_name},  // Never r# prefixed
49        {"Key", RsTypePath(ctx, key_type)},
50        {"Value", RsTypePath(ctx, value_type)},
51        {"view_lifetime", ViewLifetime(accessor_case)},
52        {"view_self", ViewReceiver(accessor_case)},
53        {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
54        {"getter",
55         [&] {
56           if (ctx.is_upb()) {
57             ctx.Emit(R"rs(
58                     pub fn $field$($view_self$)
59                       -> $pb$::MapView<$view_lifetime$, $Key$, $Value$> {
60                       unsafe {
61                         let f = $pbr$::upb_MiniTable_GetFieldByIndex(
62                           <Self as $pbr$::AssociatedMiniTable>::mini_table(),
63                           $upb_mt_field_index$);
64                         $pbr$::upb_Message_GetMap(self.raw_msg(), f)
65                           .map_or_else(
66                             $pbr$::empty_map::<$Key$, $Value$>,
67                             |raw| $pb$::MapView::from_raw($pbi$::Private, raw)
68                           )
69                       }
70                     })rs");
71           } else {
72             ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}}, R"rs(
73                     pub fn $field$($view_self$)
74                       -> $pb$::MapView<$view_lifetime$, $Key$, $Value$> {
75                       unsafe {
76                         $pb$::MapView::from_raw($pbi$::Private,
77                           $getter_thunk$(self.raw_msg()))
78                       }
79                     })rs");
80           }
81         }},
82        {"getter_mut",
83         [&] {
84           if (accessor_case == AccessorCase::VIEW) {
85             return;
86           }
87           if (ctx.is_upb()) {
88             ctx.Emit({}, R"rs(
89                     pub fn $field$_mut(&mut self)
90                       -> $pb$::MapMut<'_, $Key$, $Value$> {
91                       unsafe {
92                         let parent_mini_table =
93                           <Self as $pbr$::AssociatedMiniTable>::mini_table();
94 
95                         let f =
96                           $pbr$::upb_MiniTable_GetFieldByIndex(
97                               parent_mini_table,
98                               $upb_mt_field_index$);
99 
100                         let map_entry_mini_table =
101                           $pbr$::upb_MiniTable_SubMessage(
102                               parent_mini_table,
103                               f);
104 
105                         let raw_map =
106                           $pbr$::upb_Message_GetOrCreateMutableMap(
107                               self.raw_msg(),
108                               map_entry_mini_table,
109                               f,
110                               self.arena().raw()).unwrap();
111                         let inner = $pbr$::InnerMapMut::new(
112                           raw_map, self.arena());
113                         $pb$::MapMut::from_inner($pbi$::Private, inner)
114                       }
115                     })rs");
116           } else {
117             ctx.Emit({{"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}},
118                      R"rs(
119                     pub fn $field$_mut(&mut self)
120                       -> $pb$::MapMut<'_, $Key$, $Value$> {
121                       let inner = $pbr$::InnerMapMut::new(
122                         unsafe { $getter_mut_thunk$(self.raw_msg()) });
123                       unsafe { $pb$::MapMut::from_inner($pbi$::Private, inner) }
124                     })rs");
125           }
126         }},
127        {"setter",
128         [&] {
129           if (accessor_case == AccessorCase::VIEW) {
130             return;
131           }
132           ctx.Emit({}, R"rs(
133                 pub fn set_$raw_field_name$(&mut self, src: impl $pb$::IntoProxied<$pb$::Map<$Key$, $Value$>>) {
134                   // TODO: b/355493062 - Fix this extra copy.
135                   self.$field$_mut().copy_from(src.into_proxied($pbi$::Private).as_view());
136                 }
137               )rs");
138         }}},
139       R"rs(
140     $getter$
141     $getter_mut$
142     $setter$
143     )rs");
144 }
145 
InExternC(Context & ctx,const FieldDescriptor & field) const146 void Map::InExternC(Context& ctx, const FieldDescriptor& field) const {
147   ABSL_CHECK(ctx.is_cpp());
148 
149   ctx.Emit(
150       {
151           {"getter_thunk", ThunkName(ctx, field, "get")},
152           {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
153           {"getter",
154            [&] {
155              if (ctx.is_upb()) {
156                ctx.Emit({}, R"rs(
157                 fn $getter_thunk$(raw_msg: $pbr$::RawMessage)
158                   -> $Option$<$pbr$::RawMap>;
159                 fn $getter_mut_thunk$(raw_msg: $pbr$::RawMessage,
160                   arena: $pbr$::RawArena) -> $pbr$::RawMap;
161               )rs");
162              } else {
163                ctx.Emit({}, R"rs(
164                 fn $getter_thunk$(msg: $pbr$::RawMessage) -> $pbr$::RawMap;
165                 fn $getter_mut_thunk$(msg: $pbr$::RawMessage,) -> $pbr$::RawMap;
166               )rs");
167              }
168            }},
169       },
170       R"rs(
171     $getter$
172   )rs");
173 }
174 
InThunkCc(Context & ctx,const FieldDescriptor & field) const175 void Map::InThunkCc(Context& ctx, const FieldDescriptor& field) const {
176   ABSL_CHECK(ctx.is_cpp());
177 
178   ctx.Emit({{"field", cpp::FieldName(&field)},
179             {"Key", MapElementTypeName(*field.message_type()->map_key())},
180             {"Value", MapElementTypeName(*field.message_type()->map_value())},
181             {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
182             {"getter_thunk", ThunkName(ctx, field, "get")},
183             {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
184             {"impls",
185              [&] {
186                ctx.Emit(
187                    R"cc(
188                      const void* $getter_thunk$(const $QualifiedMsg$* msg) {
189                        return &msg->$field$();
190                      }
191                      void* $getter_mut_thunk$($QualifiedMsg$* msg) { return msg->mutable_$field$(); }
192                    )cc");
193              }}},
194            "$impls$");
195 }
196 
197 }  // namespace rust
198 }  // namespace compiler
199 }  // namespace protobuf
200 }  // namespace google
201