yazi/yazi-dds/src/stream.rs
2026-07-13 02:01:37 +08:00

101 lines
2.9 KiB
Rust

use std::{io, path::PathBuf};
use tokio::{io::{AsyncBufReadExt, BufReader, Lines, ReadHalf, WriteHalf}, sync::OnceCell};
use yazi_fs::{Xdg, create_owned_dir, engine::{Engine, local::Local}};
pub struct Stream;
#[cfg(unix)]
pub type ClientReader = Lines<BufReader<ReadHalf<tokio::net::UnixStream>>>;
#[cfg(windows)]
pub type ClientReader = Lines<BufReader<ReadHalf<tokio::net::TcpStream>>>;
#[cfg(unix)]
pub(super) type ClientWriter = WriteHalf<tokio::net::UnixStream>;
#[cfg(windows)]
pub(super) type ClientWriter = WriteHalf<tokio::net::TcpStream>;
#[cfg(unix)]
pub(super) type ServerListener = tokio::net::UnixListener;
#[cfg(windows)]
pub(super) type ServerListener = WinUnixListener;
impl Stream {
#[cfg(unix)]
pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> {
let stream = tokio::net::UnixStream::connect(Self::socket_file().await?).await?;
let (reader, writer) = tokio::io::split(stream);
Ok((BufReader::new(reader).lines(), writer))
}
#[cfg(windows)]
pub async fn connect() -> io::Result<(ClientReader, ClientWriter)> {
let p = Self::socket_file().await?;
let uds = tokio::task::spawn_blocking(move || uds_windows::UnixStream::connect(p)).await??;
let (reader, writer) = tokio::io::split(WinUnixListener::into_tokio(uds)?);
Ok((BufReader::new(reader).lines(), writer))
}
#[cfg(unix)]
pub(super) async fn bind() -> io::Result<ServerListener> {
let p = Self::socket_file().await?;
Local::regular(&p).remove_file().await.ok();
tokio::net::UnixListener::bind(p)
}
#[cfg(windows)]
pub(super) async fn bind() -> io::Result<ServerListener> {
let p = Self::socket_file().await?;
Local::regular(&p).remove_file().await.ok();
let listener = uds_windows::UnixListener::bind(p)?;
listener.set_nonblocking(true)?;
Ok(WinUnixListener(listener))
}
async fn socket_file() -> io::Result<&'static PathBuf> {
static ONCE: OnceCell<PathBuf> = OnceCell::const_new();
ONCE
.get_or_try_init(|| async move {
let p = Xdg::runtime_dir();
create_owned_dir(p).await?;
Ok(p.join(".dds.sock"))
})
.await
}
}
// --- WinUnixListener
#[cfg(windows)]
pub(super) struct WinUnixListener(uds_windows::UnixListener);
#[cfg(windows)]
impl WinUnixListener {
pub(super) async fn accept(
&self,
) -> io::Result<(tokio::net::TcpStream, uds_windows::SocketAddr)> {
loop {
match self.0.accept() {
Ok((stream, addr)) => return Ok((Self::into_tokio(stream)?, addr)),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
tokio::time::sleep(std::time::Duration::from_millis(20)).await;
}
Err(e) => return Err(e),
}
}
}
fn into_tokio(uds: uds_windows::UnixStream) -> io::Result<tokio::net::TcpStream> {
use std::os::windows::io::{FromRawSocket, IntoRawSocket};
let raw = uds.into_raw_socket();
let std = unsafe { std::net::TcpStream::from_raw_socket(raw) };
std.set_nonblocking(true)?;
tokio::net::TcpStream::from_std(std)
}
}