1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2024 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 "absl/strings/string_view.h"
12 #include "google/protobuf/compiler/cpp/helpers.h"
13 #include "google/protobuf/compiler/rust/accessors/accessor_case.h"
14 #include "google/protobuf/compiler/rust/accessors/default_value.h"
15 #include "google/protobuf/compiler/rust/accessors/generator.h"
16 #include "google/protobuf/compiler/rust/accessors/with_presence.h"
17 #include "google/protobuf/compiler/rust/context.h"
18 #include "google/protobuf/compiler/rust/naming.h"
19 #include "google/protobuf/compiler/rust/upb_helpers.h"
20 #include "google/protobuf/descriptor.h"
21
22 namespace google {
23 namespace protobuf {
24 namespace compiler {
25 namespace rust {
26
InMsgImpl(Context & ctx,const FieldDescriptor & field,AccessorCase accessor_case) const27 void SingularCord::InMsgImpl(Context& ctx, const FieldDescriptor& field,
28 AccessorCase accessor_case) const {
29 if (field.has_presence()) {
30 WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case);
31 }
32
33 std::string field_name = FieldNameWithCollisionAvoidance(field);
34 bool is_string_type = field.type() == FieldDescriptor::TYPE_STRING;
35 ctx.Emit({{"field", RsSafeName(field_name)},
36 {"raw_field_name", field_name},
37 {"proxied_type", RsTypePath(ctx, field)},
38 {"default_value", DefaultValue(ctx, field)},
39 {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
40 {"borrowed_type",
41 [&] {
42 if (is_string_type) {
43 ctx.Emit("$pb$::ProtoStr");
44 } else {
45 ctx.Emit("[u8]");
46 }
47 }},
48 {"transform_borrowed",
49 [&] {
50 if (is_string_type) {
51 ctx.Emit(R"rs(
52 $pb$::ProtoStringCow::Borrowed(
53 // SAFETY: The runtime doesn't require ProtoStr to be UTF-8.
54 unsafe { $pb$::ProtoStr::from_utf8_unchecked(view.as_ref()) }
55 )
56 )rs");
57 } else {
58 ctx.Emit(R"rs(
59 $pb$::ProtoBytesCow::Borrowed(
60 unsafe { view.as_ref() }
61 )
62 )rs");
63 }
64 }},
65 {"transform_owned",
66 [&] {
67 if (is_string_type) {
68 ctx.Emit(R"rs(
69 $pb$::ProtoStringCow::Owned(
70 $pb$::ProtoString::from_inner($pbi$::Private, inner)
71 )
72 )rs");
73 } else {
74 ctx.Emit(R"rs(
75 $pb$::ProtoBytesCow::Owned(
76 $pb$::ProtoBytes::from_inner($pbi$::Private, inner)
77 )
78 )rs");
79 }
80 }},
81 {"view_lifetime", ViewLifetime(accessor_case)},
82 {"view_type",
83 [&] {
84 if (is_string_type) {
85 ctx.Emit("$pb$::ProtoStringCow<$view_lifetime$>");
86 } else {
87 ctx.Emit("$pb$::ProtoBytesCow<$view_lifetime$>");
88 }
89 }},
90 {"view_self", ViewReceiver(accessor_case)},
91 {"getter_impl",
92 [&] {
93 if (ctx.is_cpp()) {
94 ctx.Emit(
95 {{"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")},
96 {"borrowed_getter_thunk",
97 ThunkName(ctx, field, "get_cord_borrowed")},
98 {"owned_getter_thunk",
99 ThunkName(ctx, field, "get_cord_owned")}},
100 R"rs(
101 let cord_is_flat = unsafe { $is_flat_thunk$(self.raw_msg()) };
102 if cord_is_flat {
103 let view = unsafe { $borrowed_getter_thunk$(self.raw_msg()) };
104 return $transform_borrowed$;
105 }
106
107 let owned = unsafe { $owned_getter_thunk$(self.raw_msg()) };
108 let inner = unsafe { $pbr$::InnerProtoString::from_raw(owned) };
109
110 $transform_owned$
111 )rs");
112 } else {
113 ctx.Emit(R"rs(
114 let view = unsafe {
115 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
116 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
117 $upb_mt_field_index$);
118 $pbr$::upb_Message_GetString(
119 self.raw_msg(), f, ($default_value$).into())
120 };
121 $transform_borrowed$
122 )rs");
123 }
124 }},
125 {"getter",
126 [&] {
127 ctx.Emit(R"rs(
128 pub fn $field$($view_self$) -> $view_type$ {
129 $getter_impl$
130 }
131 )rs");
132 }},
133 {"setter_impl",
134 [&] {
135 if (ctx.is_cpp()) {
136 ctx.Emit({{"setter_thunk", ThunkName(ctx, field, "set")}},
137 R"rs(
138 let s = val.into_proxied($pbi$::Private);
139 unsafe {
140 $setter_thunk$(
141 self.as_mutator_message_ref($pbi$::Private).msg(),
142 s.into_inner($pbi$::Private).into_raw()
143 );
144 }
145 )rs");
146 } else {
147 ctx.Emit(R"rs(
148 let s = val.into_proxied($pbi$::Private);
149 let (view, arena) =
150 s.into_inner($pbi$::Private).into_raw_parts();
151
152 let mm_ref =
153 self.as_mutator_message_ref($pbi$::Private);
154 let parent_arena = mm_ref.arena();
155
156 parent_arena.fuse(&arena);
157
158 unsafe {
159 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
160 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
161 $upb_mt_field_index$);
162 $pbr$::upb_Message_SetBaseFieldString(
163 self.as_mutator_message_ref($pbi$::Private).msg(),
164 f,
165 view);
166 }
167 )rs");
168 }
169 }},
170 {"setter",
171 [&] {
172 if (accessor_case == AccessorCase::VIEW) return;
173 ctx.Emit({},
174 R"rs(
175 pub fn set_$raw_field_name$(&mut self, val: impl $pb$::IntoProxied<$proxied_type$>) {
176 $setter_impl$
177 }
178 )rs");
179 }}},
180 R"rs(
181 $getter$
182 $setter$
183 )rs");
184 }
185
InExternC(Context & ctx,const FieldDescriptor & field) const186 void SingularCord::InExternC(Context& ctx, const FieldDescriptor& field) const {
187 ABSL_CHECK(ctx.is_cpp());
188
189 if (field.has_presence()) {
190 WithPresenceAccessorsInExternC(ctx, field);
191 }
192
193 ctx.Emit(
194 {{"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")},
195 {"owned_getter_thunk", ThunkName(ctx, field, "get_cord_owned")},
196 {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")},
197 {"getter_thunk", ThunkName(ctx, field, "get")},
198 {"setter_thunk", ThunkName(ctx, field, "set")},
199 {"setter",
200 [&] {
201 if (ctx.is_cpp()) {
202 ctx.Emit(R"rs(
203 fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::CppStdString);
204 )rs");
205 } else {
206 ctx.Emit(R"rs(
207 fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::PtrAndLen);
208 )rs");
209 }
210 }},
211 {"getter_thunks",
212 [&] {
213 if (ctx.is_cpp()) {
214 ctx.Emit(R"rs(
215 fn $is_flat_thunk$(raw_msg: $pbr$::RawMessage) -> bool;
216 fn $borrowed_getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::PtrAndLen;
217 fn $owned_getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::CppStdString;
218 )rs");
219 } else {
220 ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}}, R"rs(
221 fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::PtrAndLen;
222 )rs");
223 }
224 }}},
225 R"rs(
226 $getter_thunks$
227 $setter$
228 )rs");
229 }
230
InThunkCc(Context & ctx,const FieldDescriptor & field) const231 void SingularCord::InThunkCc(Context& ctx, const FieldDescriptor& field) const {
232 ABSL_CHECK(ctx.is_cpp());
233
234 if (field.has_presence()) {
235 WithPresenceAccessorsInThunkCc(ctx, field);
236 }
237
238 ctx.Emit(
239 {{"field", cpp::FieldName(&field)},
240 {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
241 {"setter_thunk", ThunkName(ctx, field, "set")},
242 {"borrowed_getter_thunk", ThunkName(ctx, field, "get_cord_borrowed")},
243 {"owned_getter_thunk", ThunkName(ctx, field, "get_cord_owned")},
244 {"is_flat_thunk", ThunkName(ctx, field, "cord_is_flat")}},
245 R"cc(
246 bool $is_flat_thunk$($QualifiedMsg$* msg) {
247 const absl::Cord& cord = msg->$field$();
248 return cord.TryFlat().has_value();
249 }
250 ::google::protobuf::rust::PtrAndLen $borrowed_getter_thunk$($QualifiedMsg$* msg) {
251 const absl::Cord& cord = msg->$field$();
252 absl::string_view s = cord.TryFlat().value();
253 return ::google::protobuf::rust::PtrAndLen{s.data(), s.size()};
254 }
255 std::string* $owned_getter_thunk$($QualifiedMsg$* msg) {
256 const absl::Cord& cord = msg->$field$();
257 std::string* owned = new std::string();
258 absl::CopyCordToString(cord, owned);
259 return owned;
260 }
261 void $setter_thunk$($QualifiedMsg$* msg, std::string* s) {
262 msg->set_$field$(absl::Cord(std::move(*s)));
263 delete s;
264 }
265 )cc");
266 }
267
268 } // namespace rust
269 } // namespace compiler
270 } // namespace protobuf
271 } // namespace google
272