• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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