use base64::Engine;
use buffa::Message;
use connectrpc::Context;
use http::header::HeaderValue;
use crate::query::Detail;
pub const QUERY_DETAIL_RESPONSE_HEADER: &str = "x-store-query-detail-bin";
pub fn encode_query_detail_header_value(detail: &Detail) -> String {
base64::engine::general_purpose::STANDARD_NO_PAD.encode(detail.encode_to_vec())
}
pub fn decode_query_detail_header_value(s: &str) -> Result<Detail, String> {
let bytes = base64::engine::general_purpose::STANDARD_NO_PAD
.decode(s)
.or_else(|_| base64::engine::general_purpose::STANDARD.decode(s))
.map_err(|e| e.to_string())?;
Detail::decode_from_slice(&bytes).map_err(|e| e.to_string())
}
pub fn with_query_detail_response_header(mut ctx: Context, detail: &Detail) -> Context {
if let Ok(v) = HeaderValue::from_str(&encode_query_detail_header_value(detail)) {
if let Ok(name) = http::HeaderName::from_bytes(QUERY_DETAIL_RESPONSE_HEADER.as_bytes()) {
ctx.response_headers.insert(name, v);
}
}
ctx
}
pub fn with_query_detail_trailer(mut ctx: Context, detail: &Detail) -> Context {
if let Ok(v) = HeaderValue::from_str(&encode_query_detail_header_value(detail)) {
if let Ok(name) = http::HeaderName::from_bytes(QUERY_DETAIL_RESPONSE_HEADER.as_bytes()) {
ctx.set_trailer(name, v);
}
}
ctx
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn round_trip_detail_header() {
let detail = Detail {
sequence_number: 42,
read_stats: [("read_bytes".to_string(), 3)].into_iter().collect(),
..Default::default()
};
let s = encode_query_detail_header_value(&detail);
let back = decode_query_detail_header_value(&s).unwrap();
assert_eq!(back.sequence_number, detail.sequence_number);
assert_eq!(back.read_stats.get("read_bytes"), Some(&3u64));
}
}