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>>; #[cfg(windows)] pub type ClientReader = Lines>>; #[cfg(unix)] pub(super) type ClientWriter = WriteHalf; #[cfg(windows)] pub(super) type ClientWriter = WriteHalf; #[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 { 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 { 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 = 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 { 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) } }