Skip to main content

skiff_cli/session/
client.rs

1//! Session IPC client (one request per connection).
2
3use std::io::{Read, Write};
4use std::os::unix::net::UnixStream;
5use std::time::Duration;
6
7use serde_json::Value;
8
9use crate::error::{Error, Result};
10use crate::session::paths::session_sock_path;
11use crate::session::protocol::{SessionRequest, SessionResponse};
12
13const CONNECT_TIMEOUT: Duration = Duration::from_secs(5);
14const REQUEST_TIMEOUT: Duration = Duration::from_secs(300);
15
16/// Send one NDJSON request to a named session daemon and return the result.
17pub fn session_request(name: &str, method: &str, params: Value) -> Result<Value> {
18    let sock = session_sock_path(name);
19    if !sock.exists() {
20        return Err(Error::runtime(format!(
21            "session {name:?} is not running (socket missing). Start with --session-start {name}"
22        )));
23    }
24
25    let mut stream = UnixStream::connect(&sock).map_err(|e| {
26        Error::runtime(format!(
27            "cannot connect to session {name:?}: {e}. Start with --session-start {name}"
28        ))
29    })?;
30    stream
31        .set_read_timeout(Some(REQUEST_TIMEOUT))
32        .map_err(|e| Error::runtime(e.to_string()))?;
33    stream
34        .set_write_timeout(Some(CONNECT_TIMEOUT))
35        .map_err(|e| Error::runtime(e.to_string()))?;
36
37    let req = SessionRequest {
38        id: 1,
39        method: method.to_string(),
40        params,
41    };
42    let mut line = serde_json::to_string(&req)?;
43    line.push('\n');
44    stream
45        .write_all(line.as_bytes())
46        .map_err(|e| Error::runtime(format!("session write failed: {e}")))?;
47    let _ = stream.shutdown(std::net::Shutdown::Write);
48
49    let mut buf = String::new();
50    stream
51        .read_to_string(&mut buf)
52        .map_err(|e| Error::runtime(format!("session read failed: {e}")))?;
53    let line = buf.lines().next().unwrap_or("").trim();
54    if line.is_empty() {
55        return Err(Error::runtime(format!(
56            "session {name:?} closed without a response"
57        )));
58    }
59    let resp: SessionResponse = serde_json::from_str(line)
60        .map_err(|e| Error::runtime(format!("invalid session response: {e}")))?;
61    if let Some(err) = resp.error {
62        return Err(Error::runtime(err));
63    }
64    Ok(resp.result.unwrap_or(Value::Null))
65}