use super::raw_bytes::{ProstField, RawBytes};
use crate::types::message::{Message, MessageType};
use crate::types::protos::Status;
#[derive(Clone, PartialEq, Debug, Default)]
pub struct Response<Payload: ProstField + Default = RawBytes> {
pub status: Option<Status>,
pub payload: Payload,
}
impl<Payload: ProstField + Default> Message for Response<Payload> {
const TYPE_ID: MessageType = MessageType::Response;
}
impl Response<()> {
pub fn error(status: Status) -> Self {
Self {
status: Some(status),
payload: (),
}
}
}
impl<Payload: ProstField + Default> Response<Payload> {
pub fn ok(payload: Payload) -> Self {
let status = None;
Self { status, payload }
}
}
impl<Payload: ProstField + Default> ::prost::Message for Response<Payload> {
fn encode_raw(&self, buf: &mut impl ::prost::bytes::BufMut) {
if let Some(ref msg) = self.status {
::prost::encoding::message::encode(1u32, msg, buf);
}
if !self.payload.is_empty() {
self.payload.encode(2u32, buf);
}
}
fn merge_field(
&mut self,
tag: u32,
wire_type: ::prost::encoding::WireType,
buf: &mut impl ::prost::bytes::Buf,
ctx: ::prost::encoding::DecodeContext,
) -> ::core::result::Result<(), ::prost::DecodeError> {
const STRUCT_NAME: &str = "Response";
match tag {
1u32 => {
let value = &mut self.status;
::prost::encoding::message::merge(
wire_type,
value.get_or_insert_with(::core::default::Default::default),
buf,
ctx,
)
.map_err(|mut error| {
error.push(STRUCT_NAME, "status");
error
})
}
2u32 => {
let value = &mut self.payload;
value.merge(wire_type, buf, ctx).map_err(|mut error| {
error.push(STRUCT_NAME, "payload");
error
})
}
_ => ::prost::encoding::skip_field(wire_type, tag, buf, ctx),
}
}
#[inline]
fn encoded_len(&self) -> usize {
self.status
.as_ref()
.map_or(0, |msg| ::prost::encoding::message::encoded_len(1u32, msg))
+ if !self.payload.is_empty() {
self.payload.encoded_len(2u32)
} else {
0
}
}
fn clear(&mut self) {
self.status = ::core::option::Option::None;
self.payload.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::types::encoding::Encodeable as _;
const PAYLOAD_KEY: u8 = (2 << 3) | 2;
#[test]
fn empty_payload_field_is_omitted() {
let response = Response::ok(());
let bytes = response.encode_to_bytes().expect("encode");
assert!(bytes.is_empty(), "OK response with empty payload is empty");
assert_eq!(prost::Message::encoded_len(&response), bytes.len());
let status = Status::internal("boom");
let error = Response::error(status.clone());
let bytes = error.encode_to_bytes().expect("encode");
assert_eq!(
bytes.len(),
::prost::encoding::message::encoded_len(1u32, &status),
"an error response must carry only the status field, no empty payload field"
);
}
#[test]
fn non_empty_payload_field_is_encoded() {
let response = Response::ok(Status::internal("payload")); let bytes = response.encode_to_bytes().expect("encode");
assert_eq!(bytes[0], PAYLOAD_KEY);
assert_eq!(prost::Message::encoded_len(&response), bytes.len());
}
#[derive(Clone, PartialEq, ::prost::Message)]
struct ResponseTwin {
#[prost(message, optional, tag = "1")]
status: Option<Status>,
#[prost(bytes = "vec", tag = "2")]
payload: Vec<u8>,
}
#[test]
fn wire_roundtrip_matches_prost_derive() {
use crate::types::encoding::Decodeable as _;
use crate::types::protos::raw_bytes::RawBytes;
let inner = Status::internal("some payload");
let ours = Response {
status: Some(Status::new(crate::Code::Aborted, "stop")),
payload: inner.clone(),
};
let bytes = ours.encode_to_bytes().expect("encode");
let twin = <ResponseTwin as prost::Message>::decode(bytes).expect("twin decodes ours");
assert_eq!(twin.status, ours.status);
let payload =
<Status as prost::Message>::decode(&twin.payload[..]).expect("payload decodes");
assert_eq!(payload, inner);
let mut twin_bytes = Vec::new();
prost::Message::encode(&twin, &mut twin_bytes).expect("twin encodes");
let back = Response::<RawBytes>::decode(&twin_bytes[..]).expect("we decode the twin");
assert_eq!(back.status, ours.status);
assert_eq!(
back.payload.decode::<Status>().expect("payload decodes"),
inner
);
}
}