use alloc::vec::Vec;
use crate::service_gen::ServiceCatalog;
pub const REFLECTION_PATH: &str = "/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo";
pub const STATUS_NOT_FOUND: i32 = 5;
#[must_use]
pub fn encode_list_services(catalog: &ServiceCatalog) -> Vec<u8> {
let mut inner = Vec::new();
for name in catalog.fully_qualified_service_names() {
let mut svc_buf = Vec::new();
write_string_field(&mut svc_buf, 1, &name);
write_length_delimited_field(&mut inner, 1, &svc_buf);
}
let mut out = Vec::new();
write_length_delimited_field(&mut out, 6, &inner);
out
}
#[must_use]
pub fn encode_error(code: i32, message: &str) -> Vec<u8> {
let mut inner = Vec::new();
write_varint_field(&mut inner, 1, code as u64);
write_string_field(&mut inner, 2, message);
let mut out = Vec::new();
write_length_delimited_field(&mut out, 7, &inner);
out
}
fn write_varint(out: &mut Vec<u8>, mut v: u64) {
while v >= 0x80 {
out.push((v as u8) | 0x80);
v >>= 7;
}
out.push(v as u8);
}
fn write_tag(out: &mut Vec<u8>, field: u32, wire_type: u8) {
let tag = (field << 3) | u32::from(wire_type);
write_varint(out, u64::from(tag));
}
fn write_varint_field(out: &mut Vec<u8>, field: u32, v: u64) {
write_tag(out, field, 0); write_varint(out, v);
}
fn write_string_field(out: &mut Vec<u8>, field: u32, s: &str) {
write_length_delimited_field(out, field, s.as_bytes());
}
fn write_length_delimited_field(out: &mut Vec<u8>, field: u32, bytes: &[u8]) {
write_tag(out, field, 2); write_varint(out, bytes.len() as u64);
out.extend_from_slice(bytes);
}
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
use crate::service_gen::TopicService;
#[test]
fn empty_catalog_yields_empty_list_services_response() {
let cat = ServiceCatalog::new();
let bytes = encode_list_services(&cat);
assert_eq!(bytes, vec![0x32, 0x00]);
}
#[test]
fn one_service_in_list() {
let mut cat = ServiceCatalog::new();
cat.register(TopicService::from_topic("Trade", "Trade"));
let bytes = encode_list_services(&cat);
let name = "zerodds.bridge.v1.TradeStream";
let inner_total = 2 + 2 + name.len(); assert_eq!(bytes[0], 0x32);
assert_eq!(bytes[1] as usize, inner_total);
}
#[test]
fn error_response_encodes_code_and_message() {
let bytes = encode_error(STATUS_NOT_FOUND, "not found");
assert_eq!(bytes[0], 0x3a);
assert!(bytes.windows(1).any(|w| w[0] == 0x08));
}
#[test]
fn varint_basics() {
let mut v = Vec::new();
write_varint(&mut v, 0);
assert_eq!(v, vec![0]);
let mut v = Vec::new();
write_varint(&mut v, 127);
assert_eq!(v, vec![127]);
let mut v = Vec::new();
write_varint(&mut v, 128);
assert_eq!(v, vec![0x80, 0x01]);
let mut v = Vec::new();
write_varint(&mut v, 300);
assert_eq!(v, vec![0xac, 0x02]);
}
#[test]
fn reflection_path_constant_matches_grpcurl_default() {
assert_eq!(
REFLECTION_PATH,
"/grpc.reflection.v1alpha.ServerReflection/ServerReflectionInfo"
);
}
}