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