use std::fmt::Display;
use tokio::{
io::{AsyncBufReadExt, AsyncWriteExt, BufReader},
sync::mpsc::Receiver,
};
use interprocess::local_socket::tokio::prelude::*;
use super::LOCAL_DEV;
pub enum DevtoolsMessage {
Authenticated(bool),
ServerPort(u16),
AuthError(serde_json::Value),
}
pub enum Message {
Login,
}
impl Display for Message {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Login => write!(f, "login"),
}
}
}
pub async fn start<F: FnMut(DevtoolsMessage) + Send + 'static>(
mut outgoing_message_rx: Receiver<Message>,
mut on_message: F,
) -> Result<(), std::io::Error> {
#[cfg(windows)]
let name = {
use interprocess::local_socket::ToNsName;
"cn-devtools-premium.sock".to_ns_name::<interprocess::local_socket::GenericNamespaced>()?
};
#[cfg(not(windows))]
let name = {
use interprocess::local_socket::ToFsName;
if let Ok(name) = std::env::temp_dir()
.join("cn-devtools-premium.socket")
.to_fs_name::<interprocess::local_socket::GenericFilePath>()
{
name
} else {
"/tmp/cn-devtools-premium.sock"
.to_fs_name::<interprocess::local_socket::GenericFilePath>()?
}
};
let max_attempts = 10;
let mut i = 0;
let conn = loop {
match LocalSocketStream::connect(name.clone()).await {
Ok(c) => break c,
Err(e)
if e.kind() == std::io::ErrorKind::ConnectionRefused
|| e.kind() == std::io::ErrorKind::NotFound =>
{
if LOCAL_DEV {
println!("retrying...");
}
if i == max_attempts {
return Err(e);
}
std::thread::sleep(std::time::Duration::from_millis(300));
i += 1;
}
Err(e) => return Err(e),
}
};
let (receiver, mut sender) = conn.split();
let mut reader = BufReader::new(receiver);
std::thread::spawn(move || {
tauri::async_runtime::spawn(async move {
let mut buf = String::with_capacity(128);
loop {
tokio::select! {
res = reader.read_line(&mut buf) => {
match res {
Ok(0) => break,
Ok(_) => {
buf.pop();
if LOCAL_DEV {
println!("devtools message: {buf}");
}
if let Some(authenticated) = buf
.strip_prefix("authenticated:")
.and_then(|b| b.parse().ok())
{
on_message(DevtoolsMessage::Authenticated(authenticated));
} else if let Some(port) =
buf.strip_prefix("port:").and_then(|b| b.parse().ok())
{
on_message(DevtoolsMessage::ServerPort(port));
} else if let Some(payload) = buf
.strip_prefix("auth-error:")
.and_then(|b| serde_json::from_str::<serde_json::Value>(b).ok())
{
on_message(DevtoolsMessage::AuthError(payload));
}
buf.clear();
}
Err(e) => {
if LOCAL_DEV {
eprintln!("failed to read IPC line: {e}");
}
break;
}
}
}
Some(message) = outgoing_message_rx.recv() => {
let write_message = format!("{message}\n");
if LOCAL_DEV {
println!("sending to devtools app: {write_message:?}");
}
if let Err(e) = sender.write_all(write_message.as_bytes()).await {
if LOCAL_DEV {
eprintln!("failed to send message: {e}");
}
}
}
}
}
});
});
Ok(())
}