whipplescript_custody/
client.rs1use std::io::{BufRead, BufReader, Read, Write};
8use std::os::unix::net::UnixStream;
9use std::path::PathBuf;
10
11use crate::{CustodyCall, CustodyReply, CustodyTransport, TransportError};
12
13const MAX_LINE_BYTES: usize = 4 * 1024 * 1024;
15
16pub use crate::CUSTODIAN_SOCKET_ENV;
20
21pub struct UnixSocketTransport {
22 socket_path: PathBuf,
23}
24
25impl UnixSocketTransport {
26 pub fn new(socket_path: PathBuf) -> Self {
27 Self { socket_path }
28 }
29
30 pub fn from_env() -> Option<Self> {
32 std::env::var_os(CUSTODIAN_SOCKET_ENV).map(|path| Self::new(PathBuf::from(path)))
33 }
34}
35
36impl CustodyTransport for UnixSocketTransport {
37 fn call(&self, call: CustodyCall) -> Result<CustodyReply, TransportError> {
38 let stream = UnixStream::connect(&self.socket_path)
39 .map_err(|e| TransportError::Unavailable(e.to_string()))?;
40 let mut writer = stream
41 .try_clone()
42 .map_err(|e| TransportError::Unavailable(e.to_string()))?;
43 let wire = serde_json::to_string(&call)
44 .map_err(|e| TransportError::Protocol(format!("unserializable call: {e}")))?;
45 writer
46 .write_all(wire.as_bytes())
47 .and_then(|_| writer.write_all(b"\n"))
48 .map_err(|e| TransportError::Unavailable(e.to_string()))?;
49 let mut reader = BufReader::new(stream).take(MAX_LINE_BYTES as u64);
50 let mut line = String::new();
51 reader
52 .read_line(&mut line)
53 .map_err(|e| TransportError::Unavailable(e.to_string()))?;
54 let value: serde_json::Value = serde_json::from_str(line.trim())
55 .map_err(|e| TransportError::Protocol(format!("malformed reply: {e}")))?;
56 if let Some(detail) = value.get("protocol_error").and_then(|v| v.as_str()) {
57 return Err(TransportError::Protocol(detail.to_string()));
58 }
59 serde_json::from_value(value)
60 .map_err(|e| TransportError::Protocol(format!("malformed reply: {e}")))
61 }
62}