use prost::Message;
use serde::{Deserialize, Serialize};
use serde_with::{base64::Base64, serde_as};
#[serde_as]
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
pub struct Key {
#[prost(bytes = "bytes", tag = "1")]
#[serde_as(as = "Base64")]
pub key: bytes::Bytes,
}
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppendRequest {
#[prost(message, repeated, tag = "1")]
pub records: Vec<Record>,
#[prost(bool, tag = "2")]
#[serde(default)]
pub await_durable: bool,
}
#[serde_as]
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Record {
#[prost(bytes = "bytes", optional, tag = "1")]
#[serde_as(as = "Option<Base64>")]
pub key: Option<bytes::Bytes>,
#[prost(bytes = "bytes", optional, tag = "2")]
#[serde_as(as = "Option<Base64>")]
pub value: Option<bytes::Bytes>,
}
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct AppendResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(int32, tag = "2")]
pub records_appended: i32,
#[prost(uint64, tag = "3")]
pub start_sequence: u64,
}
impl AppendResponse {
pub fn success(records_appended: i32, start_sequence: u64) -> Self {
Self {
status: "success".to_string(),
records_appended,
start_sequence,
}
}
}
#[serde_as]
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ScanResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(bytes = "bytes", optional, tag = "2")]
#[serde_as(as = "Option<Base64>")]
pub key: Option<bytes::Bytes>,
#[prost(message, repeated, tag = "3")]
pub values: Vec<Value>,
}
impl ScanResponse {
pub fn success(key: bytes::Bytes, values: Vec<Value>) -> Self {
Self {
status: "success".to_string(),
key: Some(key),
values,
}
}
}
#[serde_as]
#[derive(Clone, PartialEq, Message, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Value {
#[prost(uint64, tag = "1")]
pub sequence: u64,
#[prost(bytes = "bytes", tag = "2")]
#[serde_as(as = "Base64")]
pub value: bytes::Bytes,
}
#[derive(Clone, PartialEq, Message, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SegmentsResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(message, repeated, tag = "2")]
pub segments: Vec<Segment>,
}
impl SegmentsResponse {
pub fn success(segments: Vec<Segment>) -> Self {
Self {
status: "success".to_string(),
segments,
}
}
}
#[derive(Clone, PartialEq, Message, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct Segment {
#[prost(uint32, tag = "1")]
pub id: u32,
#[prost(uint64, tag = "2")]
pub start_seq: u64,
#[prost(int64, tag = "3")]
pub start_time_ms: i64,
}
#[derive(Clone, PartialEq, Message, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct KeysResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(message, repeated, tag = "2")]
pub keys: Vec<Key>,
}
impl KeysResponse {
pub fn success(keys: Vec<bytes::Bytes>) -> Self {
Self {
status: "success".to_string(),
keys: keys.into_iter().map(|k| Key { key: k }).collect(),
}
}
}
#[derive(Clone, PartialEq, Message, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CountResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(uint64, tag = "2")]
pub count: u64,
}
impl CountResponse {
pub fn success(count: u64) -> Self {
Self {
status: "success".to_string(),
count,
}
}
}
#[allow(dead_code)]
#[derive(Clone, PartialEq, Message)]
pub struct ErrorResponse {
#[prost(string, tag = "1")]
pub status: String,
#[prost(string, tag = "2")]
pub message: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn should_encode_and_decode_append_request() {
let request = AppendRequest {
records: vec![Record {
key: Some(bytes::Bytes::from("test-key")),
value: Some(bytes::Bytes::from("test-value")),
}],
await_durable: true,
};
let encoded = request.encode_to_vec();
let decoded = AppendRequest::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.records.len(), 1);
assert_eq!(
*decoded.records[0].key.as_ref().unwrap(),
bytes::Bytes::from("test-key")
);
assert_eq!(
*decoded.records[0].value.as_ref().unwrap(),
bytes::Bytes::from("test-value")
);
assert!(decoded.await_durable);
}
#[test]
fn should_encode_and_decode_append_response() {
let response = AppendResponse {
status: "success".to_string(),
records_appended: 5,
start_sequence: 42,
};
let encoded = response.encode_to_vec();
let decoded = AppendResponse::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.status, "success");
assert_eq!(decoded.records_appended, 5);
assert_eq!(decoded.start_sequence, 42);
}
#[test]
fn should_encode_and_decode_scan_response() {
let response = ScanResponse {
status: "success".to_string(),
key: Some(bytes::Bytes::from("my-key")),
values: vec![Value {
sequence: 10,
value: bytes::Bytes::from("my-value"),
}],
};
let encoded = response.encode_to_vec();
let decoded = ScanResponse::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.status, "success");
assert_eq!(*decoded.key.as_ref().unwrap(), bytes::Bytes::from("my-key"));
assert_eq!(decoded.values.len(), 1);
assert_eq!(decoded.values[0].sequence, 10);
assert_eq!(decoded.values[0].value, bytes::Bytes::from("my-value"));
}
#[test]
fn should_encode_and_decode_keys_response() {
let response = KeysResponse {
status: "success".to_string(),
keys: vec![
Key {
key: bytes::Bytes::from("key-a"),
},
Key {
key: bytes::Bytes::from("key-b"),
},
],
};
let encoded = response.encode_to_vec();
let decoded = KeysResponse::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.status, "success");
assert_eq!(decoded.keys.len(), 2);
assert_eq!(decoded.keys[0].key, bytes::Bytes::from("key-a"));
assert_eq!(decoded.keys[1].key, bytes::Bytes::from("key-b"));
}
#[test]
fn should_encode_and_decode_segments_response() {
let response = SegmentsResponse {
status: "success".to_string(),
segments: vec![Segment {
id: 0,
start_seq: 0,
start_time_ms: 1705766400000,
}],
};
let encoded = response.encode_to_vec();
let decoded = SegmentsResponse::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.status, "success");
assert_eq!(decoded.segments.len(), 1);
assert_eq!(decoded.segments[0].id, 0);
assert_eq!(decoded.segments[0].start_seq, 0);
assert_eq!(decoded.segments[0].start_time_ms, 1705766400000);
}
#[test]
fn should_encode_and_decode_error_response() {
let response = ErrorResponse {
status: "error".to_string(),
message: "Something went wrong".to_string(),
};
let encoded = response.encode_to_vec();
let decoded = ErrorResponse::decode(encoded.as_slice()).unwrap();
assert_eq!(decoded.status, "error");
assert_eq!(decoded.message, "Something went wrong");
}
}