use serde::de::{self, MapAccess, SeqAccess, Visitor};
use serde::{Deserialize, Deserializer, Serialize};
use std::path::PathBuf;
use crate::{SessionName, WindowTarget};
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct NewWindowRequest {
pub target: SessionName,
pub name: Option<String>,
pub detached: bool,
#[serde(default)]
pub environment: Option<Vec<String>>,
#[serde(default)]
pub command: Option<Vec<String>>,
#[serde(default)]
pub start_directory: Option<PathBuf>,
#[serde(default)]
pub target_window_index: Option<u32>,
#[serde(default)]
pub insert_at_target: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct KillWindowRequest {
pub target: WindowTarget,
pub kill_all_others: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SelectWindowRequest {
pub target: WindowTarget,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RenameWindowRequest {
pub target: WindowTarget,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct NextWindowRequest {
pub target: SessionName,
#[serde(default)]
pub alerts_only: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct PreviousWindowRequest {
pub target: SessionName,
#[serde(default)]
pub alerts_only: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LastWindowRequest {
pub target: SessionName,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ListWindowsRequest {
pub target: SessionName,
pub format: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LinkWindowRequest {
pub source: WindowTarget,
pub target: WindowTarget,
#[serde(default)]
pub after: bool,
#[serde(default)]
pub before: bool,
#[serde(default)]
pub kill_destination: bool,
#[serde(default)]
pub detached: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum MoveWindowTarget {
Session(SessionName),
Window(WindowTarget),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct MoveWindowRequest {
pub source: Option<WindowTarget>,
pub target: MoveWindowTarget,
pub renumber: bool,
pub kill_destination: bool,
pub detached: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SwapWindowRequest {
pub source: WindowTarget,
pub target: WindowTarget,
pub detached: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum RotateWindowDirection {
Down,
Up,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct RotateWindowRequest {
pub target: WindowTarget,
pub direction: RotateWindowDirection,
#[serde(default)]
pub restore_zoom: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ResizeWindowRequest {
pub target: WindowTarget,
pub width: Option<u16>,
pub height: Option<u16>,
#[serde(default)]
pub adjustment: Option<ResizeWindowAdjustment>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResizeWindowAdjustment {
Up(u16),
Down(u16),
Left(u16),
Right(u16),
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct RespawnWindowRequest {
pub target: WindowTarget,
#[serde(default)]
pub kill: bool,
#[serde(default)]
pub environment: Option<Vec<String>>,
#[serde(default)]
pub command: Option<Vec<String>>,
#[serde(default)]
pub start_directory: Option<PathBuf>,
}
impl<'de> Deserialize<'de> for NewWindowRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_struct(
"NewWindowRequest",
&[
"target",
"name",
"detached",
"environment",
"command",
"start_directory",
"target_window_index",
"insert_at_target",
],
NewWindowRequestVisitor,
)
}
}
impl<'de> Deserialize<'de> for RespawnWindowRequest {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
deserializer.deserialize_struct(
"RespawnWindowRequest",
&[
"target",
"kill",
"environment",
"command",
"start_directory",
],
RespawnWindowRequestVisitor,
)
}
}
struct NewWindowRequestVisitor;
impl<'de> Visitor<'de> for NewWindowRequestVisitor {
type Value = NewWindowRequest;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a new-window request")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let target = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let name = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let detached = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let environment = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(3, &self))?;
let command = compat_next_element(&mut seq)?;
let start_directory = compat_next_element(&mut seq)?;
let target_window_index = compat_next_element(&mut seq)?;
let insert_at_target = compat_next_element(&mut seq)?;
Ok(NewWindowRequest {
target,
name,
detached,
environment,
command,
start_directory,
target_window_index,
insert_at_target,
})
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut target = None;
let mut name = None;
let mut detached = None;
let mut environment = None;
let mut command = None;
let mut start_directory = None;
let mut target_window_index = None;
let mut insert_at_target = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"target" => target = Some(map.next_value()?),
"name" => name = Some(map.next_value()?),
"detached" => detached = Some(map.next_value()?),
"environment" => environment = Some(map.next_value()?),
"command" => command = Some(map.next_value()?),
"start_directory" => start_directory = Some(map.next_value()?),
"target_window_index" => target_window_index = Some(map.next_value()?),
"insert_at_target" => insert_at_target = Some(map.next_value()?),
_ => {
let _: de::IgnoredAny = map.next_value()?;
}
}
}
Ok(NewWindowRequest {
target: target.ok_or_else(|| de::Error::missing_field("target"))?,
name: name.ok_or_else(|| de::Error::missing_field("name"))?,
detached: detached.ok_or_else(|| de::Error::missing_field("detached"))?,
environment: environment.ok_or_else(|| de::Error::missing_field("environment"))?,
command: command.unwrap_or_default(),
start_directory: start_directory.unwrap_or_default(),
target_window_index: target_window_index.unwrap_or_default(),
insert_at_target: insert_at_target.unwrap_or_default(),
})
}
}
struct RespawnWindowRequestVisitor;
impl<'de> Visitor<'de> for RespawnWindowRequestVisitor {
type Value = RespawnWindowRequest;
fn expecting(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
formatter.write_str("a respawn-window request")
}
fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
where
A: SeqAccess<'de>,
{
let target = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(0, &self))?;
let kill = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(1, &self))?;
let environment = seq
.next_element()?
.ok_or_else(|| de::Error::invalid_length(2, &self))?;
let command = compat_next_element(&mut seq)?;
let start_directory = compat_next_element(&mut seq)?;
Ok(RespawnWindowRequest {
target,
kill,
environment,
command,
start_directory,
})
}
fn visit_map<A>(self, mut map: A) -> Result<Self::Value, A::Error>
where
A: MapAccess<'de>,
{
let mut target = None;
let mut kill = None;
let mut environment = None;
let mut command = None;
let mut start_directory = None;
while let Some(key) = map.next_key::<String>()? {
match key.as_str() {
"target" => target = Some(map.next_value()?),
"kill" => kill = Some(map.next_value()?),
"environment" => environment = Some(map.next_value()?),
"command" => command = Some(map.next_value()?),
"start_directory" => start_directory = Some(map.next_value()?),
_ => {
let _: de::IgnoredAny = map.next_value()?;
}
}
}
Ok(RespawnWindowRequest {
target: target.ok_or_else(|| de::Error::missing_field("target"))?,
kill: kill.ok_or_else(|| de::Error::missing_field("kill"))?,
environment: environment.ok_or_else(|| de::Error::missing_field("environment"))?,
command: command.unwrap_or_default(),
start_directory: start_directory.unwrap_or_default(),
})
}
}
fn compat_next_element<'de, A, T>(seq: &mut A) -> Result<T, A::Error>
where
A: SeqAccess<'de>,
T: Deserialize<'de> + Default,
{
match seq.next_element::<T>() {
Ok(Some(value)) => Ok(value),
Ok(None) => Ok(T::default()),
Err(error) if is_truncated_compat_sequence(&error) => Ok(T::default()),
Err(error) => Err(error),
}
}
fn is_truncated_compat_sequence(error: &impl std::fmt::Display) -> bool {
let message = error.to_string();
message.contains("UnexpectedEof") || message.contains("unexpected end of file")
}