hx-remote 0.1.2

Open files in new or existing Helix sessions through a tiny LSP bridge
Documentation
use crate::{SocketRequest, SocketResponse};
use std::io::{self, BufRead, BufReader, ErrorKind, Read, Write};
use std::net::Shutdown;
use std::os::unix::net::UnixStream;
use std::path::Path;
use std::time::Duration;

const RESPONSE_LIMIT: u64 = 64 * 1024;

pub fn socket_is_listening(socket_path: &Path) -> io::Result<bool> {
    match UnixStream::connect(socket_path) {
        Ok(_) => Ok(true),
        Err(error)
            if matches!(
                error.kind(),
                ErrorKind::NotFound | ErrorKind::ConnectionRefused
            ) || error.raw_os_error() == Some(libc::ENOTSOCK) =>
        {
            Ok(false)
        }
        Err(error) => Err(io::Error::new(
            error.kind(),
            format!("cannot check {}: {error}", socket_path.display()),
        )),
    }
}

pub fn send_socket_request(socket_path: &Path, request: &SocketRequest) -> io::Result<String> {
    let mut stream = UnixStream::connect(socket_path).map_err(|error| {
        io::Error::new(
            error.kind(),
            format!(
                "cannot connect to {}: {error}; is a receiving Helix session running?",
                socket_path.display()
            ),
        )
    })?;
    stream.set_read_timeout(Some(Duration::from_secs(10)))?;
    stream.set_write_timeout(Some(Duration::from_secs(10)))?;

    serde_json::to_writer(&mut stream, request)
        .map_err(|error| io::Error::new(ErrorKind::InvalidData, error))?;
    stream.write_all(b"\n")?;
    stream.flush()?;
    stream.shutdown(Shutdown::Write)?;

    let mut response_bytes = Vec::new();
    BufReader::new(stream)
        .take(RESPONSE_LIMIT + 1)
        .read_until(b'\n', &mut response_bytes)?;
    if response_bytes.len() as u64 > RESPONSE_LIMIT {
        return Err(io::Error::new(
            ErrorKind::InvalidData,
            "bridge response was unexpectedly large",
        ));
    }
    if response_bytes.is_empty() {
        return Err(io::Error::new(
            ErrorKind::UnexpectedEof,
            "bridge closed the connection without a response",
        ));
    }

    let response: SocketResponse = serde_json::from_slice(&response_bytes)
        .map_err(|error| io::Error::new(ErrorKind::InvalidData, error))?;
    if response.ok {
        Ok(response.message)
    } else {
        Err(io::Error::other(response.message))
    }
}