use prost::Message;
#[derive(Clone, PartialEq, Eq, Message)]
pub struct Header {
#[prost(string, required, tag = "1")]
pub key: String,
#[prost(string, required, tag = "2")]
pub value: String,
}
impl Header {
pub fn new(key: impl Into<String>, value: impl Into<String>) -> Self {
Self {
key: key.into(),
value: value.into(),
}
}
}
#[derive(Clone, PartialEq, Message)]
pub struct Frame {
#[prost(uint64, required, tag = "1")]
pub seq_id: u64,
#[prost(uint64, required, tag = "2")]
pub log_id: u64,
#[prost(int32, required, tag = "3")]
pub service: i32,
#[prost(int32, required, tag = "4")]
pub method: i32,
#[prost(message, repeated, tag = "5")]
pub headers: Vec<Header>,
#[prost(string, optional, tag = "6")]
pub payload_encoding: Option<String>,
#[prost(string, optional, tag = "7")]
pub payload_type: Option<String>,
#[prost(bytes = "vec", optional, tag = "8")]
pub payload: Option<Vec<u8>>,
#[prost(string, optional, tag = "9")]
pub log_id_new: Option<String>,
}
impl Frame {
pub fn header(&self, key: &str) -> Option<&str> {
self.headers
.iter()
.find(|h| h.key == key)
.map(|h| h.value.as_str())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn frame_round_trips_through_encode_decode() {
let frame = Frame {
seq_id: 42,
log_id: 7,
service: 1,
method: 1,
headers: vec![
Header::new("type", "event"),
Header::new("message_id", "m-1"),
],
payload_encoding: None,
payload_type: None,
payload: Some(b"{\"hello\":\"world\"}".to_vec()),
log_id_new: None,
};
let mut buf = Vec::new();
frame.encode(&mut buf).expect("encode succeeds");
let decoded = Frame::decode(buf.as_slice()).expect("decode succeeds");
assert_eq!(decoded.seq_id, 42);
assert_eq!(decoded.log_id, 7);
assert_eq!(decoded.service, 1);
assert_eq!(decoded.method, 1);
assert_eq!(decoded.header("type"), Some("event"));
assert_eq!(decoded.header("message_id"), Some("m-1"));
assert_eq!(
decoded.payload.as_deref(),
Some(b"{\"hello\":\"world\"}".as_slice())
);
}
#[test]
fn empty_optional_strings_decode_as_some_empty_not_error() {
let frame = Frame {
seq_id: 1,
log_id: 1,
service: 1,
method: 0,
headers: vec![],
payload_encoding: Some(String::new()),
payload_type: Some(String::new()),
payload: None,
log_id_new: Some(String::new()),
};
let mut buf = Vec::new();
frame.encode(&mut buf).expect("encode succeeds");
let decoded = Frame::decode(buf.as_slice()).expect("decode succeeds");
assert_eq!(decoded.payload_encoding.as_deref(), Some(""));
assert_eq!(decoded.payload_type.as_deref(), Some(""));
assert_eq!(decoded.log_id_new.as_deref(), Some(""));
}
#[test]
fn decode_zero_fills_a_missing_required_field_rather_than_erroring() {
let mut buf = Vec::new();
prost::encoding::uint64::encode(1, &1u64, &mut buf); let decoded =
Frame::decode(buf.as_slice()).expect("decode succeeds despite missing required fields");
assert_eq!(decoded.seq_id, 1);
assert_eq!(decoded.log_id, 0);
assert_eq!(decoded.service, 0);
assert_eq!(decoded.method, 0);
}
}