use praxis_core::grpc::GrpcKind;
#[derive(Clone, Debug)]
pub struct GrpcErrorMapping {
content_type: http::HeaderValue,
include_message: bool,
}
impl GrpcErrorMapping {
pub fn new(kind: GrpcKind, include_message: bool) -> Self {
Self {
content_type: content_type_for(kind),
include_message,
}
}
pub fn with_content_type(content_type: http::HeaderValue, include_message: bool) -> Self {
Self {
content_type,
include_message,
}
}
pub fn content_type(&self) -> &http::HeaderValue {
&self.content_type
}
pub fn include_message(&self) -> bool {
self.include_message
}
}
fn content_type_for(kind: GrpcKind) -> http::HeaderValue {
http::HeaderValue::from_static(match kind {
GrpcKind::GrpcProto => "application/grpc+proto",
GrpcKind::GrpcJson => "application/grpc+json",
GrpcKind::Grpc | GrpcKind::GrpcOther | GrpcKind::None => "application/grpc",
})
}
#[cfg(test)]
#[expect(clippy::allow_attributes, reason = "blanket test suppressions")]
#[allow(clippy::unwrap_used, reason = "tests use unwrap for brevity")]
mod tests {
use super::*;
#[test]
fn content_type_echoes_the_request_codec() {
for (kind, expected) in [
(GrpcKind::Grpc, "application/grpc"),
(GrpcKind::GrpcProto, "application/grpc+proto"),
(GrpcKind::GrpcJson, "application/grpc+json"),
(GrpcKind::GrpcOther, "application/grpc"),
(GrpcKind::None, "application/grpc"),
] {
let mapping = GrpcErrorMapping::new(kind, true);
assert_eq!(mapping.content_type(), expected, "{kind:?} should answer {expected}");
}
}
#[test]
fn explicit_content_type_overrides_the_codec() {
let mapping = GrpcErrorMapping::with_content_type(http::HeaderValue::from_static("application/grpc"), false);
assert_eq!(mapping.content_type(), "application/grpc", "explicit value should win");
assert!(!mapping.include_message(), "include_message should be preserved");
}
}