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 "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 SingularString::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 ctx.Emit(
35 {
36 {"field", RsSafeName(field_name)},
37 {"raw_field_name", field_name},
38 {"default_value", DefaultValue(ctx, field)},
39 {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
40 {"proxied_type", RsTypePath(ctx, field)},
41 io::Printer::Sub("transform_view",
42 [&] {
43 if (field.type() == FieldDescriptor::TYPE_STRING) {
44 ctx.Emit(R"rs(
45 // SAFETY: The runtime doesn't require ProtoStr to be UTF-8.
46 unsafe { $pb$::ProtoStr::from_utf8_unchecked(str_view.as_ref()) }
47 )rs");
48 } else {
49 ctx.Emit("unsafe { str_view.as_ref() }");
50 }
51 })
52 .WithSuffix(""), // This lets `$transform_view$,` work.
53 {"view_lifetime", ViewLifetime(accessor_case)},
54 {"view_self", ViewReceiver(accessor_case)},
55 {"getter",
56 [&] {
57 if (ctx.is_cpp()) {
58 ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}},
59 R"rs(
60 pub fn $field$($view_self$) -> $pb$::View<$view_lifetime$, $proxied_type$> {
61 let str_view = unsafe { $getter_thunk$(self.raw_msg()) };
62 $transform_view$
63 })rs");
64 } else {
65 ctx.Emit(R"rs(
66 pub fn $field$($view_self$) -> $pb$::View<$view_lifetime$, $proxied_type$> {
67 let str_view = unsafe {
68 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
69 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
70 $upb_mt_field_index$);
71 $pbr$::upb_Message_GetString(
72 self.raw_msg(), f, ($default_value$).into())
73 };
74 $transform_view$
75 })rs");
76 }
77 }},
78 {"setter_impl",
79 [&] {
80 if (ctx.is_cpp()) {
81 ctx.Emit({{"setter_thunk", ThunkName(ctx, field, "set")}},
82 R"rs(
83 let s = val.into_proxied($pbi$::Private);
84 unsafe {
85 $setter_thunk$(
86 self.as_mutator_message_ref($pbi$::Private).msg(),
87 s.into_inner($pbi$::Private).into_raw()
88 );
89 }
90 )rs");
91 } else {
92 ctx.Emit(R"rs(
93 let s = val.into_proxied($pbi$::Private);
94 let (view, arena) =
95 s.into_inner($pbi$::Private).into_raw_parts();
96
97 let mm_ref =
98 self.as_mutator_message_ref($pbi$::Private);
99 let parent_arena = mm_ref.arena();
100
101 parent_arena.fuse(&arena);
102
103 unsafe {
104 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
105 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
106 $upb_mt_field_index$);
107 $pbr$::upb_Message_SetBaseFieldString(
108 self.as_mutator_message_ref($pbi$::Private).msg(),
109 f,
110 view);
111 }
112 )rs");
113 }
114 }},
115 {"setter",
116 [&] {
117 if (accessor_case == AccessorCase::VIEW) return;
118 ctx.Emit(R"rs(
119 pub fn set_$raw_field_name$(&mut self, val: impl $pb$::IntoProxied<$proxied_type$>) {
120 $setter_impl$
121 }
122 )rs");
123 }},
124 },
125 R"rs(
126 $getter$
127 $setter$
128 )rs");
129 }
130
InExternC(Context & ctx,const FieldDescriptor & field) const131 void SingularString::InExternC(Context& ctx,
132 const FieldDescriptor& field) const {
133 ABSL_CHECK(ctx.is_cpp());
134
135 if (field.has_presence()) {
136 WithPresenceAccessorsInExternC(ctx, field);
137 }
138
139 ctx.Emit(
140 {
141 {"getter_thunk", ThunkName(ctx, field, "get")},
142 {"setter_thunk", ThunkName(ctx, field, "set")},
143 {"setter",
144 [&] {
145 if (ctx.is_cpp()) {
146 ctx.Emit(R"rs(
147 fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::CppStdString);
148 )rs");
149 } else {
150 ctx.Emit(R"rs(
151 fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $pbr$::PtrAndLen);
152 )rs");
153 }
154 }},
155 },
156 R"rs(
157 fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::PtrAndLen;
158 $setter$
159 )rs");
160 }
161
InThunkCc(Context & ctx,const FieldDescriptor & field) const162 void SingularString::InThunkCc(Context& ctx,
163 const FieldDescriptor& field) const {
164 ABSL_CHECK(ctx.is_cpp());
165
166 if (field.has_presence()) {
167 WithPresenceAccessorsInThunkCc(ctx, field);
168 }
169
170 ctx.Emit(
171 {
172 {"field", cpp::FieldName(&field)},
173 {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
174 {"getter_thunk", ThunkName(ctx, field, "get")},
175 {"setter_thunk", ThunkName(ctx, field, "set")},
176 },
177 R"cc(
178 ::google::protobuf::rust::PtrAndLen $getter_thunk$($QualifiedMsg$* msg) {
179 absl::string_view val = msg->$field$();
180 return ::google::protobuf::rust::PtrAndLen{val.data(), val.size()};
181 }
182 void $setter_thunk$($QualifiedMsg$* msg, std::string* s) {
183 msg->set_$field$(std::move(*s));
184 delete s;
185 }
186 )cc");
187 }
188
189 } // namespace rust
190 } // namespace compiler
191 } // namespace protobuf
192 } // namespace google
193