#![cfg_attr(docsrs, feature(doc_cfg))]
#![doc = include_str!("../README.md")]
use std::borrow::Cow;
#[cfg(feature = "async")]
use futures_util::io::{AsyncRead, AsyncWrite};
mod error;
mod unix;
#[cfg(feature = "experimental-xcb")]
mod x11;
pub use error::{ConnError, EvalError};
pub struct Client(Inner);
pub type EvalResponse = Result<Vec<u8>, Vec<u8>>;
enum Inner {
Unix(unix::Client),
X11(x11::Client),
}
impl Client {
pub fn open(display: Option<&str>) -> Result<Self, ConnError> {
let display = get_display(display)?;
match unix::Client::open(&display) {
Ok(client) => Ok(Self(Inner::Unix(client))),
Err(err) => x11::Client::fallback(&display, err)
.map(|client| Self(Inner::X11(client))),
}
}
pub fn eval(
&mut self,
form: impl AsRef<[u8]>,
) -> Result<EvalResponse, EvalError> {
match &mut self.0 {
Inner::Unix(client) => client.eval(form.as_ref(), false),
Inner::X11(client) => client.eval(form.as_ref(), false),
}
}
pub fn send(&mut self, form: impl AsRef<[u8]>) -> Result<(), EvalError> {
match &mut self.0 {
Inner::Unix(client) => client.eval(form.as_ref(), true).map(|_| ()),
Inner::X11(client) => client.eval(form.as_ref(), true).map(|_| ()),
}
}
}
#[inline]
pub fn open(display: Option<&str>) -> Result<Client, ConnError> {
Client::open(display)
}
#[cfg(feature = "async")]
pub struct AsyncClient<S>(unix::AsyncClient<S>);
#[cfg(feature = "tokio")]
pub type TokioClient =
AsyncClient<tokio_util::compat::Compat<tokio::net::UnixStream>>;
#[cfg(feature = "tokio")]
impl AsyncClient<tokio_util::compat::Compat<tokio::net::UnixStream>> {
pub async fn open(display: Option<&str>) -> Result<Self, ConnError> {
let display = get_display(display)?;
unix::AsyncClient::open(&display).await.map(Self)
}
}
#[cfg(feature = "tokio")]
#[inline]
pub async fn open_tokio(
display: Option<&str>,
) -> Result<TokioClient, ConnError> {
TokioClient::open(display).await
}
#[cfg(feature = "async")]
impl<S: AsyncRead + AsyncWrite + Unpin> AsyncClient<S> {
pub fn new(socket: S) -> Self { Self(unix::AsyncClient(socket)) }
pub async fn eval(
&mut self,
form: impl AsRef<[u8]>,
) -> Result<EvalResponse, EvalError> {
self.0.eval(form.as_ref(), false).await
}
pub async fn send(
&mut self,
form: impl AsRef<[u8]>,
) -> Result<(), EvalError> {
self.0.eval(form.as_ref(), true).await.map(|_| ())
}
}
#[cfg(feature = "async")]
pub fn server_path(
display: Option<&str>,
) -> Result<std::path::PathBuf, ConnError> {
get_display(display).and_then(|display| unix::server_path(&display))
}
fn get_display(
display: Option<&str>,
) -> Result<std::borrow::Cow<'_, str>, ConnError> {
display
.map(Cow::Borrowed)
.or_else(|| std::env::var("DISPLAY").map(Cow::Owned).ok())
.filter(|display| !display.is_empty())
.ok_or(ConnError::NoDisplay)
}
#[cfg(not(feature = "experimental-xcb"))]
mod x11 {
use super::*;
pub enum Client {}
impl Client {
pub fn fallback(
_display: &str,
err: ConnError,
) -> Result<Self, ConnError> {
Err(err)
}
pub fn eval(
&mut self,
_form: &[u8],
_is_async: bool,
) -> Result<EvalResponse, EvalError> {
match *self {}
}
}
}