use buffa::Message;
use bytes::Bytes;
use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::error::ConnectError;
pub mod content_type {
pub const PROTO: &str = "application/proto";
pub const JSON: &str = "application/json";
pub const CONNECT_PROTO: &str = "application/connect+proto";
pub const CONNECT_JSON: &str = "application/connect+json";
}
pub mod header {
pub const PROTOCOL_VERSION: &str = "connect-protocol-version";
pub const TIMEOUT_MS: &str = "connect-timeout-ms";
pub const CONTENT_ENCODING: &str = "connect-content-encoding";
pub const ACCEPT_ENCODING: &str = "connect-accept-encoding";
}
pub fn encode_proto<M: Message>(message: &M) -> Result<Bytes, ConnectError> {
Ok(message.encode_to_bytes())
}
pub fn decode_proto<M: Message>(data: &[u8]) -> Result<M, ConnectError> {
M::decode_from_slice(data)
.map_err(|e| ConnectError::invalid_argument(format!("failed to decode proto: {e}")))
}
pub fn encode_json<M: Serialize>(message: &M) -> Result<Bytes, ConnectError> {
serde_json::to_vec(message)
.map(Bytes::from)
.map_err(|e| ConnectError::internal(format!("failed to encode JSON: {e}")))
}
pub fn decode_json<M: DeserializeOwned>(data: &[u8]) -> Result<M, ConnectError> {
serde_json::from_slice(data)
.map_err(|e| ConnectError::invalid_argument(format!("failed to decode JSON: {e}")))
}
#[derive(Debug, Clone, Copy, Default)]
pub struct ProtoCodec;
impl ProtoCodec {
pub fn content_type() -> &'static str {
content_type::PROTO
}
pub fn encode<M: Message>(message: &M) -> Result<Bytes, ConnectError> {
encode_proto(message)
}
pub fn decode<M: Message>(data: &[u8]) -> Result<M, ConnectError> {
decode_proto(data)
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct JsonCodec;
impl JsonCodec {
pub fn content_type() -> &'static str {
content_type::JSON
}
pub fn encode<M: Serialize>(message: &M) -> Result<Bytes, ConnectError> {
encode_json(message)
}
pub fn decode<M: DeserializeOwned>(data: &[u8]) -> Result<M, ConnectError> {
decode_json(data)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum CodecFormat {
Proto,
Json,
}
impl std::fmt::Display for CodecFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Proto => write!(f, "proto"),
Self::Json => write!(f, "json"),
}
}
}
impl CodecFormat {
pub fn from_content_type(content_type: &str) -> Option<Self> {
if content_type.starts_with(content_type::PROTO)
|| content_type.starts_with(content_type::CONNECT_PROTO)
{
Some(Self::Proto)
} else if content_type.starts_with(content_type::JSON)
|| content_type.starts_with(content_type::CONNECT_JSON)
{
Some(Self::Json)
} else {
None
}
}
pub fn from_codec(codec: &str) -> Option<Self> {
match codec {
"proto" => Some(Self::Proto),
"json" => Some(Self::Json),
_ => None,
}
}
#[inline]
pub fn content_type(&self) -> &'static str {
match self {
Self::Proto => content_type::PROTO,
Self::Json => content_type::JSON,
}
}
#[inline]
pub fn streaming_content_type(&self) -> &'static str {
match self {
Self::Proto => content_type::CONNECT_PROTO,
Self::Json => content_type::CONNECT_JSON,
}
}
#[inline]
pub fn is_streaming_content_type(content_type: &str) -> bool {
content_type.starts_with(content_type::CONNECT_PROTO)
|| content_type.starts_with(content_type::CONNECT_JSON)
}
}