use std::{default::Default, fmt};
use anyhow::anyhow;
use clap::ValueEnum;
use serde_derive::{Deserialize, Serialize};
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Serialize, Deserialize, Debug)]
pub struct VersionHeader {
pub version: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub enum ConnectHeader {
Attach(AttachHeader),
List,
SessionMessage(SessionMessageRequest),
Detach(DetachRequest),
Kill(KillRequest),
SetLogLevel(SetLogLevelRequest),
GetVars,
ModifyVar(ModifyVarRequest),
}
#[derive(Serialize, Deserialize, Debug)]
pub struct KillRequest {
#[serde(default)]
pub sessions: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct KillReply {
#[serde(default)]
pub not_found_sessions: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DetachRequest {
#[serde(default)]
pub sessions: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct DetachReply {
#[serde(default)]
pub not_found_sessions: Vec<String>,
#[serde(default)]
pub not_attached_sessions: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Default, ValueEnum, Clone)]
pub enum LogLevel {
#[default]
Off,
Error,
Warn,
Info,
Debug,
Trace,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SetLogLevelRequest {
#[serde(default)]
pub level: LogLevel,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct SetLogLevelReply {}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifyVarRequest {
pub var: String,
pub val: Option<String>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ModifyVarReply {}
#[derive(Serialize, Deserialize, Debug)]
pub struct SessionMessageRequest {
#[serde(default)]
pub session_name: String,
#[serde(default)]
pub payload: SessionMessageRequestPayload,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub enum SessionMessageRequestPayload {
Resize(ResizeRequest),
#[default]
Detach,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ResizeRequest {
#[serde(default)]
pub tty_size: TtySize,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum SessionMessageReply {
NotFound,
NotAttached,
Resize(ResizeReply),
Detach(SessionMessageDetachReply),
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum SessionMessageDetachReply {
Ok,
}
#[derive(Serialize, Deserialize, Debug, PartialEq)]
pub enum ResizeReply {
Ok,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub struct AttachHeader {
#[serde(default)]
pub name: String,
#[serde(default)]
pub local_tty_size: TtySize,
#[serde(default)]
pub local_env: Vec<(String, String)>,
#[serde(default)]
pub ttl_secs: Option<u64>,
#[serde(default)]
pub cmd: Option<String>,
#[serde(default)]
pub dir: Option<String>,
}
impl AttachHeader {
pub fn local_env_get(&self, var: &str) -> Option<&str> {
self.local_env.iter().find(|(k, _)| k == var).map(|(_, v)| v.as_str())
}
}
#[derive(Serialize, Deserialize, Debug)]
pub struct AttachReplyHeader {
#[serde(default)]
pub status: AttachStatus,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct ListReply {
#[serde(default)]
pub sessions: Vec<Session>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Session {
#[serde(default)]
pub name: String,
#[serde(default)]
pub started_at_unix_ms: i64,
#[serde(default)]
pub last_connected_at_unix_ms: Option<i64>,
#[serde(default)]
pub last_disconnected_at_unix_ms: Option<i64>,
#[serde(default)]
pub status: SessionStatus,
}
#[derive(Serialize, Deserialize, Debug, Default)]
pub enum SessionStatus {
#[default]
Attached,
Disconnected,
}
impl fmt::Display for SessionStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
SessionStatus::Attached => write!(f, "attached"),
SessionStatus::Disconnected => write!(f, "disconnected"),
}
}
}
#[derive(PartialEq, Eq, Serialize, Deserialize, Debug, Clone)]
pub enum AttachStatus {
Attached { warnings: Vec<String> },
Created { warnings: Vec<String> },
Busy,
Forbidden(String),
UnexpectedError(String),
}
impl Default for AttachStatus {
fn default() -> Self {
AttachStatus::UnexpectedError(String::from("default"))
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct TtySize {
pub rows: u16,
pub cols: u16,
pub xpixel: u16,
pub ypixel: u16,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone)]
pub struct MaybeSwitch {
pub switch_to: Option<String>,
pub vars: Vec<(String, String)>,
}
#[derive(Copy, Clone, Debug, PartialEq)]
pub enum ChunkKind {
Data = 0,
Heartbeat = 1,
ExitStatus = 2,
MaybeSwitch = 3,
}
impl TryFrom<u8> for ChunkKind {
type Error = anyhow::Error;
fn try_from(v: u8) -> anyhow::Result<Self> {
match v {
0 => Ok(ChunkKind::Data),
1 => Ok(ChunkKind::Heartbeat),
2 => Ok(ChunkKind::ExitStatus),
3 => Ok(ChunkKind::MaybeSwitch),
_ => Err(anyhow!("unknown ChunkKind {}", v)),
}
}
}
#[derive(Debug, PartialEq)]
pub struct Chunk<'data> {
pub kind: ChunkKind,
pub buf: &'data [u8],
}