Adds an initial version of the proto! macro to simplify construction of proto messages in Rust. Currently supports all field types except Repeated and Map

PiperOrigin-RevId: 624995511
pull/16481/head
Derek Benson 10 months ago committed by Copybara-Service
parent 27d4fbbee9
commit e485d4f03d
  1. 1
      rust/BUILD
  2. 3
      rust/internal.rs
  3. 62
      rust/proto_macro.rs
  4. 2
      rust/shared.rs
  5. 30
      rust/test/shared/BUILD
  6. 115
      rust/test/shared/proto_macro_test.rs

@ -57,6 +57,7 @@ PROTOBUF_SHARED = [
"string.rs",
"vtable.rs",
"map.rs",
"proto_macro.rs",
]
# The Rust Protobuf runtime using the upb kernel.

@ -9,6 +9,9 @@
//! exposed to through the `protobuf` path but must be public for use by
//! generated code.
// Used by the proto! macro
pub use paste::paste;
pub use crate::r#enum::Enum;
pub use crate::vtable::{
new_vtable_field_entry, BytesMutVTable, BytesOptionalMutVTable, PrimitiveOptionalMutVTable,

@ -0,0 +1,62 @@
#[macro_export]
macro_rules! proto {
($msgtype:ty { $($tt:tt)* }) => {
$crate::proto_internal!($msgtype { $($tt)* });
}
}
#[macro_export(local_inner_macros)]
#[doc(hidden)]
macro_rules! proto_internal {
// nested message,
(@msg $msg:ident $submsg:ident : $msgtype:ty { $field:ident : $($value:tt)* }, $($rest:tt)*) => {
proto_internal!(@msg $msg $submsg : $msgtype { $field : $($value)* });
proto_internal!(@msg $msg $($rest)*);
};
// nested message
(@msg $msg:ident $submsg:ident : $msgtype:ty { $field:ident : $($value:tt)* }) => {
{
let mut $msg: <$msgtype as $crate::Proxied>::Mut<'_> = $crate::__internal::paste!($msg.[<$submsg _mut>]());
proto_internal!(@msg $msg $field : $($value)*);
}
};
// empty nested message,
(@msg $msg:ident $submsg:ident : $msgtype:ty { }, $($rest:tt)*) => {
proto_internal!(@msg $msg $submsg : $msgtype { });
proto_internal!(@msg $msg $($rest)*);
};
// empty nested message
(@msg $msg:ident $submsg:ident : $msgtype:ty { }) => {
{
let mut $msg = $crate::__internal::paste!($msg.[<$submsg _mut>]());
}
};
// field: expr,
(@msg $msg:ident $ident:ident : $expr:expr, $($rest:tt)*) => {
// delegate without ,
proto_internal!(@msg $msg $ident : $expr);
proto_internal!(@msg $msg $($rest)*);
};
// field: expr
(@msg $msg:ident $ident:ident : $expr:expr) => {
$crate::__internal::paste!{
$msg.[<set_ $ident>]($expr);
}
};
(@msg $msg:ident) => {};
// entry point
($msgtype:ty { $($tt:tt)* }) => {
{
let mut message = <$msgtype>::new();
proto_internal!(@msg message $($tt)*);
message
}
};
}

@ -26,6 +26,7 @@ pub mod __public {
pub use crate::map::{Map, MapIter, MapMut, MapView, ProxiedInMapValue};
pub use crate::optional::{AbsentField, FieldEntry, Optional, PresentField};
pub use crate::primitive::PrimitiveMut;
pub use crate::proto;
pub use crate::proxied::{
Mut, MutProxy, Proxied, ProxiedWithPresence, SettableValue, View, ViewProxy,
};
@ -59,6 +60,7 @@ mod macros;
mod map;
mod optional;
mod primitive;
mod proto_macro;
mod proxied;
mod repeated;
mod string;

@ -397,3 +397,33 @@ rust_test(
"@crate_index//:googletest",
],
)
rust_test(
name = "proto_macro_cpp_test",
srcs = ["proto_macro_test.rs"],
aliases = {
"//rust:protobuf_cpp": "protobuf",
"//rust/test/shared:matchers_cpp": "matchers",
},
deps = [
"//rust:protobuf_cpp",
"//rust/test:unittest_cc_rust_proto",
"//rust/test/shared:matchers_cpp",
"@crate_index//:googletest",
],
)
rust_test(
name = "proto_macro_upb_test",
srcs = ["proto_macro_test.rs"],
aliases = {
"//rust:protobuf_upb": "protobuf",
"//rust/test/shared:matchers_upb": "matchers",
},
deps = [
"//rust:protobuf_upb",
"//rust/test:unittest_upb_rust_proto",
"//rust/test/shared:matchers_upb",
"@crate_index//:googletest",
],
)

@ -0,0 +1,115 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
//! Tests covering accessors for singular bool, int32, int64, and bytes fields.
use googletest::prelude::*;
use protobuf::proto;
use unittest_proto::{
test_all_types::{self, NestedMessage},
NestedTestAllTypes, TestAllTypes,
};
#[test]
fn test_setting_literals() {
let fixed64 = || 108;
let msg = proto!(TestAllTypes {
optional_int32: 101,
optional_int64: 102,
optional_uint32: 103,
optional_uint64: 104,
optional_sint32: -105,
optional_sint64: 106,
optional_fixed32: 107,
optional_fixed64: fixed64(), //108
optional_sfixed32: 100 + 9,
optional_sfixed64: {
let x = 10;
100 + x
},
optional_nested_message: NestedMessage { bb: 42 },
optional_float: 111.5,
optional_double: 112000.5,
optional_bool: true,
optional_string: "foo",
optional_bytes: b"bar",
optional_nested_enum: test_all_types::NestedEnum::Baz,
});
assert_that!(msg.optional_int32(), eq(101));
assert_that!(msg.optional_int64(), eq(102));
assert_that!(msg.optional_uint32(), eq(103));
assert_that!(msg.optional_uint64(), eq(104));
assert_that!(msg.optional_sint32(), eq(-105));
assert_that!(msg.optional_sint64(), eq(106));
assert_that!(msg.optional_fixed32(), eq(107));
assert_that!(msg.optional_fixed64(), eq(108));
assert_that!(msg.optional_sfixed32(), eq(109));
assert_that!(msg.optional_sfixed64(), eq(110));
assert_that!(msg.optional_float(), eq(111.5));
assert_that!(msg.optional_double(), eq(112000.5));
assert_that!(msg.optional_bool(), eq(true));
assert_that!(msg.optional_string(), eq("foo"));
assert_that!(msg.optional_bytes(), eq(b"bar"));
assert_that!(msg.optional_nested_enum(), eq(test_all_types::NestedEnum::Baz));
}
#[test]
fn single_nested_message() {
let msg = proto!(TestAllTypes { optional_nested_message: NestedMessage { bb: 42 } });
assert_that!(msg.optional_nested_message().bb(), eq(42));
// field above it
let msg = proto!(TestAllTypes {
optional_int32: 1,
optional_nested_message: NestedMessage { bb: 42 }
});
assert_that!(msg.optional_nested_message().bb(), eq(42));
// field below it
let msg = proto!(TestAllTypes {
optional_nested_message: NestedMessage { bb: 42 },
optional_int32: 1
});
assert_that!(msg.optional_nested_message().bb(), eq(42));
// field above and below it
let msg = proto!(TestAllTypes {
optional_int32: 1,
optional_nested_message: NestedMessage { bb: 42 },
optional_int64: 2
});
assert_that!(msg.optional_nested_message().bb(), eq(42));
// test empty initializer
let msg = proto!(TestAllTypes {});
assert_that!(msg.has_optional_nested_message(), eq(false));
// empty nested message should be present
// make sure that qualified path names work
let msg = proto!(::unittest_proto::TestAllTypes {
optional_nested_message: unittest_proto::test_all_types::NestedMessage {}
});
assert_that!(msg.has_optional_nested_message(), eq(true));
}
#[test]
fn test_recursive_msg() {
let msg = proto!(NestedTestAllTypes {
child: NestedTestAllTypes {
payload: TestAllTypes { optional_int32: 41 },
child: NestedTestAllTypes {
child: NestedTestAllTypes { payload: TestAllTypes { optional_int32: 43 } },
payload: TestAllTypes { optional_int32: 42 }
}
}
});
assert_that!(msg.child().payload().optional_int32(), eq(41));
assert_that!(msg.child().child().payload().optional_int32(), eq(42));
assert_that!(msg.child().child().child().payload().optional_int32(), eq(43));
}
Loading…
Cancel
Save