diff --git a/rust/test/shared/accessors_map_test.rs b/rust/test/shared/accessors_map_test.rs index 0fc153f63bc0..50341b0c3aae 100644 --- a/rust/test/shared/accessors_map_test.rs +++ b/rust/test/shared/accessors_map_test.rs @@ -157,6 +157,23 @@ fn test_bytes_and_string_copied() { assert_that!(msg.map_int32_bytes_mut().get(1).unwrap(), eq(b"world")); } +#[test] +fn test_map_setter() { + let mut msg = TestMap::new(); + msg.map_string_string_mut().insert("hello", "world"); + msg.map_string_string_mut().insert("fizz", "buzz"); + + let mut msg2 = TestMap::new(); + msg2.set_map_string_string(msg.map_string_string()); + assert_that!( + msg2.map_string_string().iter().collect::>(), + unordered_elements_are![ + eq(("hello".into(), "world".into())), + eq(("fizz".into(), "buzz".into())) + ] + ); +} + macro_rules! generate_map_with_msg_values_tests { ( $(($k_field:ident, $k_nonzero:expr, $k_other:expr $(,)?)),* diff --git a/rust/test/shared/accessors_repeated_test.rs b/rust/test/shared/accessors_repeated_test.rs index 9aee4907ead7..99ae45374cc2 100644 --- a/rust/test/shared/accessors_repeated_test.rs +++ b/rust/test/shared/accessors_repeated_test.rs @@ -55,16 +55,17 @@ macro_rules! generate_repeated_numeric_test { #[test] fn [< test_repeated_ $field _set >]() { let mut msg = TestAllTypes::new(); - let mut mutator = msg.[](); let mut msg2 = TestAllTypes::new(); let mut mutator2 = msg2.[](); for i in 0..5 { mutator2.push(i as $t); } - protobuf::MutProxy::set(&mut mutator, mutator2.as_view()); + msg.[](mutator2.as_view()); + + let view = msg.[](); assert_that!( - mutator.iter().collect::>(), + view.iter().collect::>(), eq(mutator2.iter().collect::>()) ); } @@ -177,15 +178,16 @@ fn test_repeated_enum_accessors() { #[test] fn test_repeated_bool_set() { let mut msg = TestAllTypes::new(); - let mut mutator = msg.repeated_bool_mut(); let mut msg2 = TestAllTypes::new(); let mut mutator2 = msg2.repeated_bool_mut(); for _ in 0..5 { mutator2.push(true); } - protobuf::MutProxy::set(&mut mutator, mutator2.as_view()); - assert_that!(mutator.iter().collect::>(), eq(mutator2.iter().collect::>())); + msg.set_repeated_bool(mutator2.as_view()); + + let view = msg.repeated_bool(); + assert_that!(view.iter().collect::>(), eq(mutator2.iter().collect::>())); } #[test] diff --git a/src/google/protobuf/compiler/rust/accessors/map.cc b/src/google/protobuf/compiler/rust/accessors/map.cc index 75aa5007ee86..397d210af9b3 100644 --- a/src/google/protobuf/compiler/rust/accessors/map.cc +++ b/src/google/protobuf/compiler/rust/accessors/map.cc @@ -42,6 +42,7 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field, std::string field_name = FieldNameWithCollisionAvoidance(field); ctx.Emit({{"field", RsSafeName(field_name)}, + {"raw_field_name", field_name}, // Never r# prefixed {"Key", RsTypePath(ctx, key_type)}, {"Value", RsTypePath(ctx, value_type)}, {"view_lifetime", ViewLifetime(accessor_case)}, @@ -99,10 +100,23 @@ void Map::InMsgImpl(Context& ctx, const FieldDescriptor& field, unsafe { $pb$::MapMut::from_inner($pbi$::Private, inner) } })rs"); } + }}, + {"setter", + [&] { + if (accessor_case == AccessorCase::VIEW) { + return; + } + ctx.Emit({}, R"rs( + pub fn set_$raw_field_name$(&mut self, src: $pb$::MapView<'_, $Key$, $Value$>) { + // TODO: Implement IntoProxied and avoid copying. + self.$field$_mut().copy_from(src); + } + )rs"); }}}, R"rs( $getter$ $getter_mut$ + $setter$ )rs"); } diff --git a/src/google/protobuf/compiler/rust/accessors/repeated_field.cc b/src/google/protobuf/compiler/rust/accessors/repeated_field.cc index e1e14e646f21..11a85852ded2 100644 --- a/src/google/protobuf/compiler/rust/accessors/repeated_field.cc +++ b/src/google/protobuf/compiler/rust/accessors/repeated_field.cc @@ -23,16 +23,19 @@ namespace rust { void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field, AccessorCase accessor_case) const { std::string field_name = FieldNameWithCollisionAvoidance(field); - ctx.Emit({{"field", RsSafeName(field_name)}, - {"RsType", RsTypePath(ctx, field)}, - {"view_lifetime", ViewLifetime(accessor_case)}, - {"view_self", ViewReceiver(accessor_case)}, - {"getter_thunk", ThunkName(ctx, field, "get")}, - {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, - {"getter", - [&] { - if (ctx.is_upb()) { - ctx.Emit({}, R"rs( + ctx.Emit( + { + {"field", RsSafeName(field_name)}, + {"raw_field_name", field_name}, // Never r# prefixed + {"RsType", RsTypePath(ctx, field)}, + {"view_lifetime", ViewLifetime(accessor_case)}, + {"view_self", ViewReceiver(accessor_case)}, + {"getter_thunk", ThunkName(ctx, field, "get")}, + {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, + {"getter", + [&] { + if (ctx.is_upb()) { + ctx.Emit({}, R"rs( pub fn $field$($view_self$) -> $pb$::RepeatedView<$view_lifetime$, $RsType$> { unsafe { $getter_thunk$( @@ -47,8 +50,8 @@ void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field, ) } )rs"); - } else { - ctx.Emit({}, R"rs( + } else { + ctx.Emit({}, R"rs( pub fn $field$($view_self$) -> $pb$::RepeatedView<$view_lifetime$, $RsType$> { unsafe { $pb$::RepeatedView::from_raw( @@ -58,16 +61,16 @@ void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field, } } )rs"); - } - }}, - {"clearer_thunk", ThunkName(ctx, field, "clear")}, - {"getter_mut", - [&] { - if (accessor_case == AccessorCase::VIEW) { - return; - } - if (ctx.is_upb()) { - ctx.Emit({}, R"rs( + } + }}, + {"clearer_thunk", ThunkName(ctx, field, "clear")}, + {"getter_mut", + [&] { + if (accessor_case == AccessorCase::VIEW) { + return; + } + if (ctx.is_upb()) { + ctx.Emit({}, R"rs( pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $RsType$> { unsafe { $pb$::RepeatedMut::from_inner( @@ -85,8 +88,8 @@ void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field, } } )rs"); - } else { - ctx.Emit({}, R"rs( + } else { + ctx.Emit({}, R"rs( pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $RsType$> { unsafe { $pb$::RepeatedMut::from_inner( @@ -99,11 +102,25 @@ void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field, } } )rs"); - } - }}}, - R"rs( + } + }}, + {"setter", + [&] { + if (accessor_case == AccessorCase::VIEW) { + return; + } + ctx.Emit({}, R"rs( + pub fn set_$raw_field_name$(&mut self, src: $pb$::RepeatedView<'_, $RsType$>) { + // TODO: Implement IntoProxied and avoid copying. + self.$field$_mut().copy_from(src); + } + )rs"); + }}, + }, + R"rs( $getter$ $getter_mut$ + $setter$ )rs"); } @@ -180,6 +197,8 @@ void RepeatedField::InThunkCc(Context& ctx, {"clearer_thunk", ThunkName(ctx, field, "clear")}, {"getter_thunk", ThunkName(ctx, field, "get")}, {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}, + {"repeated_copy_from_thunk", + ThunkName(ctx, field, "repeated_copy_from")}, {"impls", [&] { ctx.Emit(