use std::io::{BufRead, BufReader, Write};
use std::os::unix::net::{UnixListener, UnixStream};
use std::path::Path;
pub fn start_if_requested() {
let Ok(path) = std::env::var("YOG_CONTROL_SOCKET") else { return };
let _ = std::fs::remove_file(&path);
let listener = match UnixListener::bind(&path) {
Ok(l) => l,
Err(e) => {
yog_logging::error!("control socket: failed to bind {}: {}", path, e);
return;
}
};
std::thread::spawn(move || {
for stream in listener.incoming().flatten() {
handle_client(stream);
}
});
}
fn write_line(stream: &mut UnixStream, json: &serde_json::Value) {
let mut line = json.to_string();
line.push('\n');
let _ = stream.write_all(line.as_bytes());
}
fn mod_list_json() -> Vec<serde_json::Value> {
crate::MOD_INFOS
.lock()
.expect("mod infos lock poisoned")
.iter()
.map(|m| serde_json::json!({ "id": m[0], "name": m[1], "version": m[2] }))
.collect()
}
fn handle_client(mut stream: UnixStream) {
let ready = serde_json::json!({
"event": "ready",
"pid": std::process::id(),
"mods": mod_list_json(),
});
write_line(&mut stream, &ready);
let reader = match stream.try_clone() {
Ok(s) => BufReader::new(s),
Err(_) => return,
};
for line in reader.lines().map_while(Result::ok) {
let Ok(cmd) = serde_json::from_str::<serde_json::Value>(&line) else { continue };
match cmd.get("cmd").and_then(|c| c.as_str()) {
Some("hot_reload") => {
let mod_id = cmd.get("mod_id").and_then(|v| v.as_str()).unwrap_or_default();
let yog_path = cmd.get("yog_path").and_then(|v| v.as_str()).unwrap_or_default();
let ok = crate::trigger_hot_reload(mod_id, Path::new(yog_path));
let response = serde_json::json!({ "event": "hot-reload-done", "mod_id": mod_id, "ok": ok });
write_line(&mut stream, &response);
}
Some("list_mods") => {
let response = serde_json::json!({ "event": "mods", "mods": mod_list_json() });
write_line(&mut stream, &response);
}
_ => {}
}
}
}