use prost::Message;
use tonic::Status;
use tonic::metadata::{BinaryMetadataKey, BinaryMetadataValue};
use tsoracle_proto::v1::{LEADER_HINT_TRAILER_KEY, LeaderHint, LeaderHintLookup};
pub fn not_leader_status(reporter: &crate::reporter::Reporter, hint: LeaderHint) -> Status {
reporter.not_leader.increment(1);
with_leader_hint(
Status::failed_precondition("not leader"),
hint,
LEADER_HINT_TRAILER_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> {
match tsoracle_proto::v1::decode_leader_hint(status) {
LeaderHintLookup::Decoded(hint) => Some(hint),
LeaderHintLookup::Absent | LeaderHintLookup::Malformed => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use tsoracle_proto::v1::EpochWire;
#[test]
fn roundtrip() {
let hint = LeaderHint {
leader_endpoint: Some("10.0.0.7:50551".into()),
leader_epoch: Some(EpochWire { hi: 0, lo: 42 }),
};
let status = not_leader_status(&crate::reporter::Reporter::for_tests(), 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() {
BinaryMetadataKey::from_bytes(LEADER_HINT_TRAILER_KEY.as_bytes())
.expect("LEADER_HINT_TRAILER_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(EpochWire { hi: 0, lo: 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());
}
}