use std::collections::HashMap;
use color_eyre::eyre::{Result, eyre};
use serde::{Deserialize, Serialize};
use tokio::io::{AsyncReadExt, AsyncWriteExt};
#[derive(Debug, Serialize, Deserialize)]
pub enum ShimMessage {
TermEvent(crossterm::event::Event),
Resize { cols: u16, rows: u16 },
Detach,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum MainMessage {
Output(Vec<u8>),
Detached,
Quit,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TerminalEnv {
pub cols: u16,
pub rows: u16,
pub font_size: Option<(u16, u16)>,
pub env_vars: HashMap<String, String>,
}
impl TerminalEnv {
pub fn capture() -> Self {
let (cols, rows) = crossterm::terminal::size().unwrap_or((80, 24));
let font_size = crossterm::terminal::window_size().ok().and_then(|ws| {
if ws.width > 0 && ws.height > 0 && ws.columns > 0 && ws.rows > 0 {
Some((ws.width / ws.columns, ws.height / ws.rows))
} else {
None
}
});
let keys = [
"TERM",
"TERM_PROGRAM",
"TERM_PROGRAM_VERSION",
"LC_TERMINAL",
"LC_TERMINAL_VERSION",
"ITERM_SESSION_ID",
"KITTY_PID",
"GHOSTTY_RESOURCES_DIR",
"WT_SESSION",
"WEZTERM_EXECUTABLE",
"COLORTERM",
"TMUX",
];
let mut env_vars = HashMap::new();
for key in keys {
if let Ok(val) = std::env::var(key)
&& !val.is_empty()
{
env_vars.insert(key.to_string(), val);
}
}
Self {
cols,
rows,
font_size,
env_vars,
}
}
}
const MAX_MESSAGE_SIZE: u32 = 64 * 1024 * 1024;
pub async fn write_message<W, M>(writer: &mut W, msg: &M) -> Result<()>
where
W: AsyncWriteExt + Unpin + Send,
M: Serialize + Sync,
{
let payload = postcard::to_stdvec(msg)?;
let len = u32::try_from(payload.len())
.map_err(|_| eyre!("message too large: {} bytes", payload.len()))?;
writer.write_all(&len.to_be_bytes()).await?;
writer.write_all(&payload).await?;
writer.flush().await?;
Ok(())
}
pub async fn read_message<R, M>(reader: &mut R) -> Result<M>
where
R: AsyncReadExt + Unpin,
M: for<'de> Deserialize<'de>,
{
let mut len_buf = [0u8; 4];
reader.read_exact(&mut len_buf).await?;
let len = u32::from_be_bytes(len_buf);
if len > MAX_MESSAGE_SIZE {
return Err(eyre!("message too large: {len} bytes"));
}
let mut payload = vec![0u8; len as usize];
reader.read_exact(&mut payload).await?;
let msg: M = postcard::from_bytes(&payload)?;
Ok(msg)
}