use prost::Message;
use tonic::Status;
use tonic::metadata::errors::InvalidMetadataKey;
use tonic::metadata::{BinaryMetadataKey, BinaryMetadataValue, MetadataKey};
use tsoracle_proto::v1::LeaderHint;
pub const KEY: &str = "tsoracle-leader-hint-bin";
pub fn validate_key() -> Result<(), InvalidMetadataKey> {
BinaryMetadataKey::from_bytes(KEY.as_bytes()).map(|_| ())
}
pub fn not_leader_status(hint: LeaderHint) -> Status {
#[cfg(feature = "metrics")]
metrics::counter!("tsoracle.not_leader.total").increment(1);
with_leader_hint(Status::failed_precondition("not leader"), hint, KEY)
}
fn with_leader_hint(mut status: Status, hint: LeaderHint, key_str: &str) -> Status {
match BinaryMetadataKey::from_bytes(key_str.as_bytes()) {
Ok(key) => {
let bytes = hint.encode_to_vec();
let value = BinaryMetadataValue::from_bytes(&bytes);
status.metadata_mut().insert_bin(key, value);
}
Err(_error) => {
#[cfg(feature = "tracing")]
tracing::error!(
key = key_str,
error = %_error,
"leader-hint metadata key invalid; omitting trailer from NOT_LEADER response"
);
}
}
status
}
pub fn decode_leader_hint(status: &Status) -> Option<LeaderHint> {
let key = MetadataKey::from_bytes(KEY.as_bytes()).ok()?;
let value = status.metadata().get_bin(key)?;
let bytes = value.to_bytes().ok()?;
LeaderHint::decode(bytes.as_ref()).ok()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip() {
let hint = LeaderHint {
leader_endpoint: Some("10.0.0.7:50551".into()),
leader_epoch: Some(42),
};
let status = not_leader_status(hint.clone());
let decoded = decode_leader_hint(&status).expect("present");
assert_eq!(decoded.leader_endpoint, hint.leader_endpoint);
assert_eq!(decoded.leader_epoch, hint.leader_epoch);
}
#[test]
fn validate_key_accepts_real_key() {
validate_key().expect("KEY is a valid binary metadata key");
}
#[test]
fn invalid_key_omits_trailer_but_preserves_status() {
let hint = LeaderHint {
leader_endpoint: Some("10.0.0.7:50551".into()),
leader_epoch: Some(42),
};
let status = with_leader_hint(
Status::failed_precondition("not leader"),
hint,
"Invalid Key",
);
assert_eq!(status.code(), tonic::Code::FailedPrecondition);
assert!(decode_leader_hint(&status).is_none());
}
}