#[cfg(test)]
use std::env;
use std::io::prelude::*;
use std::os::unix::net::{UnixListener, UnixStream};
use std::sync::{Arc, Mutex};
use std::thread;
use tempfile::{Builder, NamedTempFile};
static MSG_COMMAND: u32 = 0;
static MSG_VERSION: u32 = 7;
struct I3IpcMessage {
message_type: u32,
message_payload: String,
}
fn create_i3_message(payload: &[u8], message_type: u32) -> Vec<u8> {
let magic_string: &[u8] = b"i3-ipc";
#[allow(clippy::cast_possible_truncation)]
let length: &[u8] = &(payload.len() as u32).to_ne_bytes();
let message_type: &[u8] = &message_type.to_ne_bytes();
[magic_string, length, message_type, payload].concat()
}
fn parse_i3_message(mut socket: &UnixStream) -> Option<I3IpcMessage> {
let mut buffer = [0u8; 14];
let mut dest_size = [0u8; 4];
let mut dest_type = [0u8; 4];
let bytes_read = socket.read(&mut buffer).ok();
if bytes_read == Some(0) {
return None;
}
dest_size.clone_from_slice(&buffer[6..10]);
dest_type.clone_from_slice(&buffer[10..14]);
let message_size = u32::from_ne_bytes(dest_size);
let message_type = u32::from_ne_bytes(dest_type);
let mut dest_payload = vec![0u8; message_size as usize];
if message_size > 0 {
socket.read(&mut dest_payload).ok();
}
let payload_string = String::from_utf8_lossy(&dest_payload).into_owned();
Some(I3IpcMessage {
message_type,
message_payload: payload_string,
})
}
fn create_i3_reply(message_type: u32) -> Option<Vec<u8>> {
if message_type == MSG_VERSION {
Some(create_i3_message(
r#"{
"human_readable" : "4.2-fake",
"loaded_config_file_name" : "/tmp/fake/.i3.i3/config",
"minor" : 2,
"patch" : 0,
"major" : 4
}"#
.as_bytes(),
MSG_VERSION,
))
} else if message_type == MSG_COMMAND {
Some(create_i3_message(
r#"[{ "success": true }]"#.as_bytes(),
MSG_COMMAND,
))
} else {
None
}
}
#[must_use]
pub fn init_listener(message_log: Arc<Mutex<Vec<String>>>) -> NamedTempFile {
let socket_file = Builder::new().tempfile().unwrap();
let socket_file_name = socket_file.path().file_name().unwrap();
let listener = UnixListener::bind(socket_file_name).unwrap();
env::set_var("I3SOCK", socket_file_name);
thread::spawn(move || {
match listener.accept() {
Ok((mut socket, _)) => {
loop {
if let Some(i3_message) = parse_i3_message(&socket) {
if let Some(reply) = create_i3_reply(i3_message.message_type) {
let mut messages = message_log.lock().unwrap();
messages.push(i3_message.message_payload);
std::mem::drop(messages);
socket.write_all(&reply).ok();
}
}
}
}
Err(e) => println!("accept function failed: {:?}", e),
};
});
socket_file
}