use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
pub type JsonMeta = BTreeMap<String, String>;
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JsonStatus {
pub code: u32,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub message: String,
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct JsonFrame {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub metadata: Option<JsonMeta>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_millis: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub message: Option<Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub half_close: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub status: Option<JsonStatus>,
}
pub fn decode_json_frame(text: &str) -> Result<JsonFrame, serde_json::Error> {
serde_json::from_str(text)
}
pub fn encode_json_frame(frame: &JsonFrame) -> String {
serde_json::to_string(frame).expect("JsonFrame serializes")
}
use crate::pb::{frame::Kind, metadatum, Frame, HalfClose, Message, Metadatum, Reset, Subscribe};
fn meta_vec_to_json(items: &[Metadatum]) -> Option<JsonMeta> {
let map: JsonMeta = items
.iter()
.filter_map(|m| match &m.value {
Some(metadatum::Value::AsciiValue(s)) => Some((m.key.clone(), s.clone())),
_ => None, })
.collect();
(!map.is_empty()).then_some(map)
}
fn json_to_meta_vec(meta: &Option<JsonMeta>) -> Vec<Metadatum> {
meta.iter()
.flatten()
.map(|(k, v)| Metadatum {
key: k.clone(),
value: Some(metadatum::Value::AsciiValue(v.clone())),
})
.collect()
}
fn to_bytes(v: &Value) -> bytes::Bytes {
serde_json::to_vec(v).unwrap_or_default().into()
}
pub fn json_open_to_subscribe(f: JsonFrame, method: String) -> Subscribe {
Subscribe {
method,
headers: json_to_meta_vec(&f.metadata),
timeout_millis: f.timeout_millis.unwrap_or(0),
initial_payload: f.message.as_ref().map(to_bytes).unwrap_or_default(),
json: true,
}
}
pub fn json_frame_to_proto(f: JsonFrame) -> Frame {
let kind = if let Some(status) = f.status {
Kind::Reset(Reset { status_code: status.code, status_message: status.message })
} else if f.half_close == Some(true) {
Kind::HalfClose(HalfClose {})
} else if let Some(message) = f.message {
Kind::Message(Message { payload: to_bytes(&message) })
} else {
Kind::HalfClose(HalfClose {})
};
Frame { kind: Some(kind) }
}
pub fn proto_frame_to_json(frame: &Frame) -> Option<JsonFrame> {
let from_bytes = |b: &[u8]| serde_json::from_slice::<Value>(b).unwrap_or(Value::Null);
Some(match frame.kind.as_ref()? {
Kind::Message(m) => JsonFrame {
message: Some(from_bytes(&m.payload)),
..Default::default()
},
Kind::Header(h) => JsonFrame {
metadata: Some(meta_vec_to_json(&h.headers).unwrap_or_default()),
..Default::default()
},
Kind::Trailer(t) => JsonFrame {
status: Some(JsonStatus { code: t.status_code, message: t.status_message.clone() }),
metadata: meta_vec_to_json(&t.trailers),
..Default::default()
},
Kind::Reset(r) => JsonFrame {
status: Some(JsonStatus { code: r.status_code, message: r.status_message.clone() }),
..Default::default()
},
_ => return None,
})
}