use bytes::{BufMut, Bytes, BytesMut};
use uuid::Uuid;
use crate::protocol::codec::{read_i32_le, read_i64_le, read_string_obj};
use crate::protocol::error::Result;
use crate::protocol::messages::write_request_header;
use crate::protocol::op_code;
pub const UNKNOWN_TOP_VER: i64 = -1;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NodeEndpoint {
pub node_id: Uuid,
pub port: i32,
pub addresses: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NodeEndpointsResponse {
pub topology_version: i64,
pub added: Vec<NodeEndpoint>,
pub removed: Vec<Uuid>,
}
pub fn encode_node_endpoints_request(req_id: i64, start_top_ver: i64) -> Bytes {
let mut buf = BytesMut::new();
write_request_header(&mut buf, op_code::CLUSTER_GROUP_GET_NODE_ENDPOINTS, req_id);
buf.put_i64_le(start_top_ver);
buf.put_i64_le(UNKNOWN_TOP_VER);
buf.freeze()
}
pub fn decode_node_endpoints(buf: &mut Bytes) -> Result<NodeEndpointsResponse> {
let topology_version = read_i64_le(buf)?;
let added_cnt = read_i32_le(buf)?;
let mut added = Vec::with_capacity(added_cnt.max(0) as usize);
for _ in 0..added_cnt.max(0) {
let node_id = read_raw_uuid(buf)?;
let port = read_i32_le(buf)?;
let addr_cnt = read_i32_le(buf)?;
let mut addresses = Vec::with_capacity(addr_cnt.max(0) as usize);
for _ in 0..addr_cnt.max(0) {
if let Some(addr) = read_string_obj(buf)? {
addresses.push(addr);
}
}
added.push(NodeEndpoint {
node_id,
port,
addresses,
});
}
let removed_cnt = read_i32_le(buf)?;
let mut removed = Vec::with_capacity(removed_cnt.max(0) as usize);
for _ in 0..removed_cnt.max(0) {
removed.push(read_raw_uuid(buf)?);
}
Ok(NodeEndpointsResponse {
topology_version,
added,
removed,
})
}
fn read_raw_uuid(buf: &mut Bytes) -> Result<Uuid> {
let msb = read_i64_le(buf)? as u64;
let lsb = read_i64_le(buf)? as u64;
Ok(Uuid::from_u64_pair(msb, lsb))
}
#[cfg(test)]
mod tests {
use super::*;
use bytes::Buf;
use crate::protocol::types::type_code;
fn put_raw_uuid(buf: &mut BytesMut, u: Uuid) {
let (msb, lsb) = u.as_u64_pair();
buf.put_i64_le(msb as i64);
buf.put_i64_le(lsb as i64);
}
fn put_typed_string(buf: &mut BytesMut, s: &str) {
buf.put_u8(type_code::STRING);
buf.put_i32_le(s.len() as i32);
buf.put_slice(s.as_bytes());
}
#[test]
fn encode_request_writes_header_and_two_versions() {
let bytes = encode_node_endpoints_request(7, UNKNOWN_TOP_VER);
let mut b = bytes.clone();
assert_eq!(b.get_i16_le(), op_code::CLUSTER_GROUP_GET_NODE_ENDPOINTS);
assert_eq!(b.get_i64_le(), 7); assert_eq!(b.get_i64_le(), UNKNOWN_TOP_VER); assert_eq!(b.get_i64_le(), UNKNOWN_TOP_VER); assert_eq!(b.remaining(), 0);
}
#[test]
fn decode_full_snapshot_two_nodes() {
let n1 = Uuid::from_u128(0x1111_1111_1111_1111_1111_1111_1111_1111);
let n2 = Uuid::from_u128(0x2222_2222_2222_2222_2222_2222_2222_2222);
let mut buf = BytesMut::new();
buf.put_i64_le(5); buf.put_i32_le(2); put_raw_uuid(&mut buf, n1);
buf.put_i32_le(10801);
buf.put_i32_le(2); put_typed_string(&mut buf, "127.0.0.1");
put_typed_string(&mut buf, "host-1");
put_raw_uuid(&mut buf, n2);
buf.put_i32_le(10802);
buf.put_i32_le(1);
put_typed_string(&mut buf, "127.0.0.1");
buf.put_i32_le(0);
let mut bytes = buf.freeze();
let resp = decode_node_endpoints(&mut bytes).unwrap();
assert_eq!(resp.topology_version, 5);
assert_eq!(resp.added.len(), 2);
assert_eq!(
resp.added[0],
NodeEndpoint {
node_id: n1,
port: 10801,
addresses: vec!["127.0.0.1".into(), "host-1".into()],
}
);
assert_eq!(resp.added[1].node_id, n2);
assert_eq!(resp.added[1].port, 10802);
assert!(resp.removed.is_empty());
assert_eq!(bytes.remaining(), 0, "all bytes consumed");
}
#[test]
fn decode_with_removed_nodes() {
let removed = Uuid::from_u128(0x3333_3333_3333_3333_3333_3333_3333_3333);
let mut buf = BytesMut::new();
buf.put_i64_le(9);
buf.put_i32_le(0); buf.put_i32_le(1); put_raw_uuid(&mut buf, removed);
let mut bytes = buf.freeze();
let resp = decode_node_endpoints(&mut bytes).unwrap();
assert!(resp.added.is_empty());
assert_eq!(resp.removed, vec![removed]);
assert_eq!(bytes.remaining(), 0);
}
}