use buffa::view::MessageView;
use buffa::view::OwnedView;
use bytes::Bytes;
pub use buffa::HasMessageView;
pub struct ServiceRequest<'a, Req: HasMessageView> {
view: &'a Req::View<'a>,
body: &'a Bytes,
}
impl<'a, Req: HasMessageView> ServiceRequest<'a, Req> {
#[doc(hidden)]
pub fn from_parts(view: &'a Req::View<'a>, body: &'a Bytes) -> Self {
Self { view, body }
}
#[must_use]
pub fn view(&self) -> &'a Req::View<'a> {
self.view
}
#[must_use]
pub fn to_owned_message(&self) -> Req {
self.view.to_owned_from_source(Some(self.body))
}
#[must_use]
pub fn bytes(&self) -> &'a Bytes {
self.body
}
#[must_use]
pub fn to_owned_view(&self) -> OwnedView<Req::View<'static>> {
OwnedView::decode(self.body.clone())
.expect("ServiceRequest body was already view-decoded by the dispatch glue")
}
}
impl<Req: HasMessageView> Clone for ServiceRequest<'_, Req> {
fn clone(&self) -> Self {
*self
}
}
impl<Req: HasMessageView> Copy for ServiceRequest<'_, Req> {}
impl<'a, Req: HasMessageView> core::ops::Deref for ServiceRequest<'a, Req> {
type Target = Req::View<'a>;
fn deref(&self) -> &Req::View<'a> {
self.view
}
}
impl<'a, Req: HasMessageView> core::fmt::Debug for ServiceRequest<'a, Req>
where
Req::View<'a>: core::fmt::Debug,
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
self.view.fmt(f)
}
}
#[cfg(test)]
mod tests {
use super::*;
use buffa::Message;
use buffa_types::google::protobuf::__buffa::view::StringValueView;
use buffa_types::google::protobuf::StringValue;
fn encode(value: &str) -> Bytes {
Bytes::from(
StringValue {
value: value.into(),
..Default::default()
}
.encode_to_vec(),
)
}
#[test]
fn deref_field_access_and_view() {
let body = encode("zero-copy");
let view = StringValueView::decode_view(&body).unwrap();
let req = ServiceRequest::<StringValue>::from_parts(&view, &body);
assert_eq!(req.value, "zero-copy");
assert_eq!(req.view().value, "zero-copy");
let range = body.as_ptr_range();
assert!(range.contains(&req.value.as_ptr()));
}
#[test]
fn to_owned_message_and_bytes() {
let body = encode("keep me");
let view = StringValueView::decode_view(&body).unwrap();
let req = ServiceRequest::<StringValue>::from_parts(&view, &body);
let owned: StringValue = req.to_owned_message();
assert_eq!(owned.value, "keep me");
assert_eq!(req.bytes().as_ref(), body.as_ref());
let owned_view = req.to_owned_view();
assert_eq!(owned_view.reborrow().value, "keep me");
let range = body.as_ptr_range();
assert!(range.contains(&owned_view.reborrow().value.as_ptr()));
let copy = req;
assert_eq!(copy.value, req.value);
assert_eq!(format!("{req:?}"), format!("{copy:?}"));
}
}