use base64::Engine;
use serde_json::Value;
use std::io::{self, BufRead, Read, Write};
use std::sync::{Arc, Mutex};
const MAX_MSG: usize = 128 * 1024 * 1024;
#[derive(Clone, Copy)]
pub enum Framing {
Native,
Ndjson,
}
pub struct Peer {
w: Mutex<Box<dyn Write + Send>>,
framing: Framing,
}
pub type Out = Arc<Peer>;
impl Peer {
pub fn new(w: Box<dyn Write + Send>, framing: Framing) -> Out {
Arc::new(Peer {
w: Mutex::new(w),
framing,
})
}
pub fn native(w: Box<dyn Write + Send>) -> Out {
Self::new(w, Framing::Native)
}
pub fn ndjson(w: Box<dyn Write + Send>) -> Out {
Self::new(w, Framing::Ndjson)
}
pub fn send(&self, v: &Value) -> io::Result<()> {
let data = serde_json::to_vec(v).unwrap_or_default();
let mut o = self.w.lock().unwrap();
match self.framing {
Framing::Native => {
o.write_all(&(data.len() as u32).to_le_bytes())?;
o.write_all(&data)?;
}
Framing::Ndjson => {
o.write_all(&data)?;
o.write_all(b"\n")?;
}
}
o.flush()
}
}
pub fn send_msg(out: &Out, v: &Value) -> io::Result<()> {
out.send(v)
}
pub fn respond(out: &Out, req: &Value, mut v: Value) {
if let (Some(obj), Some(id)) = (v.as_object_mut(), req.get("id")) {
if !id.is_null() {
obj.insert("id".into(), id.clone());
}
}
crate::hostlog::record("rx", req, &v);
let _ = out.send(&v);
}
pub fn read_native<R: Read>(r: &mut R) -> Option<Value> {
let mut len = [0u8; 4];
r.read_exact(&mut len).ok()?;
let n = u32::from_le_bytes(len) as usize;
if n == 0 || n > MAX_MSG {
return None;
}
let mut buf = vec![0u8; n];
r.read_exact(&mut buf).ok()?;
serde_json::from_slice(&buf).ok()
}
pub fn read_ndjson<R: BufRead>(r: &mut R) -> Option<Value> {
loop {
let mut line = String::new();
if r.read_line(&mut line).ok()? == 0 {
return None;
}
let t = line.trim();
if t.is_empty() {
continue;
}
return Some(serde_json::from_str(t).unwrap_or(Value::Null));
}
}
pub fn b64_encode(bytes: &[u8]) -> String {
base64::engine::general_purpose::STANDARD.encode(bytes)
}
pub fn b64_decode(s: &str) -> Option<Vec<u8>> {
base64::engine::general_purpose::STANDARD.decode(s).ok()
}