Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion grpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ allowed_external_types = [
]

[features]
default = ["dns", "_runtime-tokio", "tls-rustls"]
default = ["dns", "_runtime-tokio","tls-rustls", "protobuf"]
protobuf = ["dep:protobuf"]
prost = ["dep:prost"]
dns = ["dep:hickory-resolver", "_runtime-tokio"]
# The following feature is used to ensure all modules use the runtime
# abstraction instead of using tokio directly.
Expand All @@ -41,6 +43,8 @@ tls-rustls = [
]

[dependencies]
protobuf = { version = "4.33.0-release", optional = true }
prost = { version = "0.14.0", optional = true }
bytes = "1.10.1"
hickory-resolver = { version = "0.25.1", optional = true }
http = "1.1.0"
Expand Down Expand Up @@ -89,3 +93,4 @@ tonic = { version = "0.14.0", path = "../tonic", default-features = false, featu
"router",
] }
tonic-prost = { version = "0.14.0", path = "../tonic-prost" }
protobuf-well-known-types = "4.33.0-release"
20 changes: 20 additions & 0 deletions grpc/src/server/message.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
//! Traits for accessing view and mutable view types of messages.
//!
//! These traits are needed to support Protobuf's design decision to prefer view and mut proxy types
//! over references. See <https://protobuf.dev/reference/rust/rust-design-decisions/#view-mut-proxy-types>
//! for more details.
//!
//! These traits allow for support of view and mut types while defaulting to regular references for
//! non-Protobuf classes that need gRPC support.

pub mod as_mut;
pub mod as_view;

pub use as_mut::AsMut;
pub use as_view::AsView;

#[cfg(feature = "protobuf")]
pub mod protobuf;

#[cfg(all(feature = "prost", not(feature = "protobuf")))]
pub mod prost;
78 changes: 78 additions & 0 deletions grpc/src/server/message/as_mut.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::server::message::AsView;

pub trait AsMut {
/// We let this be whatever the library uses (e.g. protobuf::Mut).
type Mut<'a>: Send + AsView + 'a;

fn as_mut(&mut self) -> Self::Mut<'_>;

/// Implement `reborrow` to make it feel it feel like a mut reference.
/// Since we can't attach .reborrow() to the alias 'Self::Mut',
/// we define the logic here as a static utility function.
#[doc(hidden)]
fn reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>
where
'a: 'b;
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_custom_as_mut() {
struct TestMsg {
val: i32,
}

struct TestMsgMut<'a> {
val: &'a mut i32,
}

impl AsView for TestMsgMut<'_> {
type View<'msg>
= i32
where
Self: 'msg;
fn as_view(&self) -> Self::View<'_> {
*self.val
}
}

impl AsMut for TestMsg {
type Mut<'a> = TestMsgMut<'a>;

fn as_mut(&mut self) -> Self::Mut<'_> {
TestMsgMut { val: &mut self.val }
}

fn reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>
where
'a: 'b,
{
TestMsgMut { val: view.val }
}
}

let mut msg = TestMsg { val: 10 };

// 1. Test as_mut()
let mut view = msg.as_mut();
assert_eq!(*view.val, 10);
*view.val = 20;

// 2. Test reborrow_view()
{
let reborrowed = <TestMsg as AsMut>::reborrow_view(&mut view);
assert_eq!(*reborrowed.val, 20);
*reborrowed.val = 30;
}

// 3. Verify view is still usable (reborrow, not move)
assert_eq!(*view.val, 30);
*view.val = 40;

// Verify changes persisted
assert_eq!(msg.val, 40);
}
}
37 changes: 37 additions & 0 deletions grpc/src/server/message/as_view.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
pub trait AsView {
/// A GAT for the view type.
type View<'msg>: Send + Copy
where
Self: 'msg;

/// Creates a view of the object.
fn as_view(&self) -> Self::View<'_>;
}

#[cfg(test)]
mod tests {
use super::AsView;

#[test]
fn test_non_protobuf_view() {
#[derive(Debug, PartialEq)]
struct TestMessage {
id: i32,
}

impl AsView for TestMessage {
type View<'msg>
= &'msg TestMessage
where
Self: 'msg;
fn as_view(&self) -> Self::View<'_> {
self
}
}

let msg = TestMessage { id: 42 };
let view = AsView::as_view(&msg);
assert_eq!(view.id, 42);
assert!(std::ptr::eq(view, &msg));
}
}
83 changes: 83 additions & 0 deletions grpc/src/server/message/prost.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use super::{AsMut, AsView};

impl<T> AsView for T
where
T: prost::Message + Sync + Send,
{
type View<'msg>
= &'msg T
where
Self: 'msg;

fn as_view(&self) -> Self::View<'_> {
self
}
}

impl<T> AsMut for T
where
T: prost::Message + Send + 'static,
{
type Mut<'a> = &'a mut T;

fn as_mut(&mut self) -> Self::Mut<'_> {
self
}

fn reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>
where
'a: 'b,
{
&mut **view
}
}

#[cfg(test)]
mod tests {
use crate::server::message::{AsMut, AsView};
use prost::Message;

// A simple prost message for testing
#[derive(Clone, PartialEq, Message)]
struct ValidateRequest {
#[prost(string, tag = "1")]
pub service_name: String,
}

#[test]
fn test_prost_view() {
let msg = ValidateRequest {
service_name: "test_service".to_string(),
};
let view = AsView::as_view(&msg);
assert_eq!(view.service_name, "test_service");
// For prost, the view is just a reference
assert_eq!(view as *const _, &msg as *const _);
}

#[test]
fn test_prost_as_mut() {
let mut msg = ValidateRequest {
service_name: "test_service".to_string(),
};

// 1. Test as_mut()
let mut view = AsMut::as_mut(&mut msg);
assert_eq!(view.service_name, "test_service");
view.service_name = "updated_service".to_string();

// 2. Test reborrow_view()
{
let reborrowed = <ValidateRequest as AsMut>::reborrow_view(&mut view);
assert_eq!(reborrowed.service_name, "updated_service");
reborrowed.service_name = "reborrowed_service".to_string();
}

// 3. Verify view is still usable
assert_eq!(view.service_name, "reborrowed_service");
view.service_name = "final_service".to_string();

// Verify changes persisted
assert_eq!(msg.service_name, "final_service");
}
}
79 changes: 79 additions & 0 deletions grpc/src/server/message/protobuf.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use super::{AsMut, AsView};

impl<T> AsView for T
where
T: protobuf::AsView + Sync,
// Require `Copy` to emulate "all shared references are copyable"
for<'msg> protobuf::View<'msg, <T as protobuf::AsView>::Proxied>: Copy,
{
type View<'msg>
= protobuf::View<'msg, <T as protobuf::AsView>::Proxied>
where
Self: 'msg;

fn as_view(&self) -> Self::View<'_> {
self.as_view()
}
}

impl<T> AsMut for T
where
T: protobuf::AsMut + Send + 'static,
for<'msg> protobuf::Mut<'msg, <T as protobuf::AsMut>::MutProxied>: Send + AsView,
{
type Mut<'a> = protobuf::Mut<'a, <T as protobuf::AsMut>::MutProxied>;

fn as_mut(&mut self) -> Self::Mut<'_> {
protobuf::AsMut::as_mut(self)
}

fn reborrow_view<'a, 'b>(view: &'b mut Self::Mut<'a>) -> Self::Mut<'b>
where
'a: 'b,
{
protobuf::AsMut::as_mut(view)
}
}

#[cfg(test)]
mod tests {
use crate::server::message::{AsMut, AsView};

#[test]
fn test_protobuf_view() {
use protobuf_well_known_types::Timestamp;
let mut ts = Timestamp::new();
ts.set_seconds(1234567890);
let view = AsView::as_view(&ts);
assert_eq!(view.seconds(), 1234567890);
}

#[test]
fn test_protobuf_as_mut() {
use protobuf_well_known_types::Timestamp;

let mut msg = Timestamp::new();
msg.set_seconds(123);
msg.set_nanos(456);

// 1. Test as_mut()
let mut view = AsMut::as_mut(&mut msg);
assert_eq!(view.seconds(), 123);
view.set_seconds(789);

// 2. Test reborrow_view()
{
let mut reborrowed = <Timestamp as AsMut>::reborrow_view(&mut view);
assert_eq!(reborrowed.seconds(), 789);
reborrowed.set_nanos(999);
}

// 3. Verify view is still usable (reborrow, not move)
assert_eq!(view.nanos(), 999);
view.set_seconds(111);

// Verify changes persisted
assert_eq!(msg.seconds(), 111);
assert_eq!(msg.nanos(), 999);
}
}
2 changes: 2 additions & 0 deletions grpc/src/server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ use crate::service::Request;
use crate::service::Response;
use crate::service::Service;

pub mod message;

pub struct Server {
handler: Option<Arc<dyn Service>>,
}
Expand Down