use crate::control_mode::constants::*;
use crate::Error;
use crate::TmuxCommand;
use std::io::BufRead;
use std::io::Lines;
use std::io::Write;
#[derive(Default, Debug, PartialEq)]
pub struct OutputBlock {
pub time: usize,
pub num: usize,
pub flags: usize,
pub success: bool,
pub data: Option<String>,
}
impl OutputBlock {}
#[derive(Debug, PartialEq)]
pub enum Response {
#[cfg(feature = "tmux_1_8")]
OutputBlockBegin {
time: usize,
num: usize,
flags: usize,
},
#[cfg(feature = "tmux_1_8")]
OutputBlockEnd {
time: usize,
num: usize,
flags: usize,
},
#[cfg(feature = "tmux_1_8")]
OutputBlockError {
time: usize,
num: usize,
flags: usize,
},
#[cfg(feature = "tmux_1_8")]
OutputBlockData(String),
#[cfg(feature = "tmux_1_8")]
OutputBlock(OutputBlock),
#[cfg(feature = "tmux_3_2")]
ClientDetached(String),
#[cfg(feature = "tmux_2_4")]
ClientSessionChanged {
client: String,
session_id: String,
name: String,
},
#[cfg(feature = "tmux_3_4")]
ConfigError(String),
#[cfg(feature = "tmux_3_2")]
Continue(String),
#[cfg(feature = "tmux_1_8")]
Exit(Option<String>),
#[cfg(feature = "tmux_3_2")]
ExtendedOutput {
pane_id: String,
age: String,
reserved: Vec<String>,
value: String,
},
#[cfg(feature = "tmux_1_8")]
LayoutChange {
window_id: String,
window_layout: String,
#[cfg(feature = "tmux_2_2")]
window_visible_layout: String,
#[cfg(feature = "tmux_2_2")]
window_flags: String,
},
#[cfg(feature = "tmux_1_8")]
Output { pane_id: String, value: String },
#[cfg(feature = "tmux_2_5")]
PaneModeChanged(String),
#[cfg(feature = "tmux_3_4")]
PasteBufferChanged(String),
#[cfg(feature = "tmux_3_4")]
PasteBufferDeleted(String),
#[cfg(feature = "tmux_3_2")]
Pause(String),
#[cfg(feature = "tmux_1_8")]
SessionChanged { session_id: String, name: String },
#[cfg(feature = "tmux_1_8")]
SessionRenamed(String),
#[cfg(feature = "tmux_2_5")]
SessionWindowChanged {
session_id: String,
window_id: String,
},
#[cfg(feature = "tmux_1_8")]
SessionsChanged,
#[cfg(feature = "tmux_3_2")]
SubscriptionChanged {
name: String,
session_id: String,
window_id: String,
window_index: String,
},
#[cfg(feature = "tmux_1_8")]
UnlinkedWindowAdd(String),
#[cfg(feature = "tmux_3_3")]
UnlinkedWindowClose(String),
#[cfg(feature = "tmux_3_3")]
UnlinkedWindowRenamed(String),
#[cfg(feature = "tmux_1_8")]
WindowAdd(String),
#[cfg(feature = "tmux_1_8")]
WindowClose(String),
#[cfg(feature = "tmux_2_5")]
WindowPaneChanged { window_id: String, pane_id: String },
#[cfg(feature = "tmux_1_8")]
WindowRenamed { window_id: String, name: String },
}
#[derive(Debug)]
pub struct ControlModeOutput<B: BufRead>(pub Lines<B>);
use std::process::ChildStdin;
impl<B: BufRead> ControlModeOutput<B> {
pub fn new(s: Lines<B>) -> Self {
ControlModeOutput(s)
}
pub fn send<'a, T: Into<TmuxCommand<'a>>>(
stdin: &mut ChildStdin,
cmd: T,
lines: &mut ControlModeOutput<B>,
) -> Result<OutputBlock, Error> {
writeln!(stdin, "{}", cmd.into())?;
match lines.next() {
Some(line) => match line {
Response::OutputBlock(data) => Ok(data),
_ => Err(Error::Tmux(String::from("error response"))),
},
None => Err(Error::Tmux(String::from("none"))),
}
}
pub fn check_main(lines: &mut Lines<B>) -> Option<Response> {
let mut _time: usize = 0;
let mut _num: usize = 0;
let mut _flags: usize = 0;
let mut output_block = OutputBlock::default();
for line in lines {
let output = line.unwrap().control_mode_line();
if let Ok(output) = output {
match output {
Response::OutputBlockBegin { time, num, flags } => {
_time = time;
_num = num;
_flags = flags;
}
Response::OutputBlockEnd { time, num, flags } => {
output_block.time = time;
output_block.num = num;
output_block.flags = flags;
output_block.success = true;
return Some(Response::OutputBlock(output_block));
}
Response::OutputBlockError { time, num, flags } => {
output_block.time = time;
output_block.num = num;
output_block.flags = flags;
output_block.success = false;
return Some(Response::OutputBlock(output_block));
}
Response::OutputBlockData(data) => output_block.data = Some(data),
other => return Some(other),
}
} else {
return None;
}
}
None
}
}
impl<B: BufRead> Iterator for ControlModeOutput<B> {
type Item = Response;
fn next(&mut self) -> Option<Self::Item> {
ControlModeOutput::check_main(&mut self.0)
}
}
pub trait ControlModeLine {
fn control_mode_line(&self) -> Result<Response, Error>;
}
fn parse_option_string(s: &str) -> Option<String> {
if !s.is_empty() {
Some(s.to_string())
} else {
None
}
}
impl<S: AsRef<str> + std::fmt::Display> ControlModeLine for S {
fn control_mode_line(&self) -> Result<Response, Error> {
match self.as_ref() {
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(OUTPUT_BLOCK_BEGIN) => {
let v: Vec<_> = s.splitn(4, CONTROL_MODE_SEPARATOR).collect();
let time = v.get(1).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let num = v.get(2).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let flags = v.get(3).ok_or(Error::CMParseNum)?.parse::<usize>()?;
Ok(Response::OutputBlockBegin { time, num, flags })
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(OUTPUT_BLOCK_END) => {
let v: Vec<_> = s.splitn(4, CONTROL_MODE_SEPARATOR).collect();
let time = v.get(1).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let num = v.get(2).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let flags = v.get(3).ok_or(Error::CMParseNum)?.parse::<usize>()?;
Ok(Response::OutputBlockEnd { time, num, flags })
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(OUTPUT_BLOCK_ERROR) => {
let v: Vec<_> = s.splitn(4, CONTROL_MODE_SEPARATOR).collect();
let time = v.get(1).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let num = v.get(2).ok_or(Error::CMParseNum)?.parse::<usize>()?;
let flags = v.get(3).ok_or(Error::CMParseNum)?.parse::<usize>()?;
Ok(Response::OutputBlockError { time, num, flags })
}
#[cfg(feature = "tmux_3_2")]
s if s.starts_with(NOTIFICATION_CLIENT_DETACHED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let client = v.get(1).ok_or(Error::CMParseStr)?;
Ok(Response::ClientDetached(client.to_string()))
}
#[cfg(feature = "tmux_2_4")]
s if s.starts_with(NOTIFICATION_CLIENT_SESSION_CHANGED) => {
let v: Vec<_> = s.splitn(4, CONTROL_MODE_SEPARATOR).collect();
let client = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let session_id = v.get(2).ok_or(Error::CMParseStr)?.to_string();
let name = v.get(3).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::ClientSessionChanged {
client,
session_id,
name,
})
}
#[cfg(feature = "tmux_3_4")]
s if s.starts_with(NOTIFICATION_CONFIG_ERROR) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let error = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::ConfigError(error))
}
#[cfg(feature = "tmux_3_2")]
s if s.starts_with(NOTIFICATION_CONTINUE) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let pane_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::Continue(pane_id))
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_EXIT) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let reason = v.get(1).map(|s| s.to_string());
Ok(Response::Exit(reason))
}
#[cfg(feature = "tmux_3_2")]
s if s.starts_with(NOTIFICATION_EXTENDED_OUTPUT) => {
let v: Vec<_> = s
.splitn(2, CONTROL_MODE_EXTENDED_OUTPUT_SEPARATOR)
.collect();
let s = v.get(0).ok_or(Error::CMParseStr)?;
let value = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let v: Vec<_> = s
.split(CONTROL_MODE_SEPARATOR)
.map(|w| w.to_string())
.collect();
let pane_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let age = v.get(2).ok_or(Error::CMParseStr)?.to_string();
let reserved = v[3..].to_vec();
Ok(Response::ExtendedOutput {
pane_id,
age,
reserved,
value,
})
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_LAYOUT_CHANGE) => {
#[cfg(feature = "tmux_2_2")]
let v: Vec<_> = s.splitn(5, CONTROL_MODE_SEPARATOR).collect();
#[cfg(all(feature = "tmux_1_8", not(feature = "tmux_2_2")))]
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let window_layout = v.get(2).ok_or(Error::CMParseStr)?.to_string();
#[cfg(feature = "tmux_2_2")]
let window_visible_layout = v.get(3).ok_or(Error::CMParseStr)?.to_string();
#[cfg(feature = "tmux_2_2")]
let window_flags = v.get(4).ok_or(Error::CMParseStr)?.to_string();
#[cfg(feature = "tmux_1_8")]
return Ok(Response::LayoutChange {
window_id,
window_layout,
#[cfg(feature = "tmux_2_2")]
window_visible_layout,
#[cfg(feature = "tmux_2_2")]
window_flags,
});
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_OUTPUT) => {
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let pane_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let value = v.get(2).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::Output { pane_id, value })
}
#[cfg(feature = "tmux_2_5")]
s if s.starts_with(NOTIFICATION_PANE_MODE_CHANGED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let pane_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::PaneModeChanged(pane_id))
}
#[cfg(feature = "tmux_3_4")]
s if s.starts_with(NOTIFICATION_PASTE_BUFFER_CHANGED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let name = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::PasteBufferChanged(name))
}
#[cfg(feature = "tmux_3_4")]
s if s.starts_with(NOTIFICATION_PASTE_BUFFER_DELETED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let name = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::PasteBufferDeleted(name))
}
#[cfg(feature = "tmux_3_2")]
s if s.starts_with(NOTIFICATION_PAUSE) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let pane_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::Pause(pane_id))
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_SESSION_CHANGED) => {
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let session_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let name = v.get(2).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::SessionChanged {
session_id,
name: name,
})
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_SESSION_RENAMED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let name = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::SessionRenamed(name))
}
#[cfg(feature = "tmux_2_5")]
s if s.starts_with(NOTIFICATION_SESSION_WINDOW_CHANGED) => {
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let session_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let window_id = v.get(2).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::SessionWindowChanged {
session_id,
window_id,
})
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_SESSIONS_CHANGED) => Ok(Response::SessionsChanged),
#[cfg(feature = "tmux_3_2")]
s if s.starts_with(NOTIFICATION_SUBSCRIPTION_CHANGED) => {
let v: Vec<_> = s.splitn(5, CONTROL_MODE_SEPARATOR).collect();
let name = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let session_id = v.get(2).ok_or(Error::CMParseStr)?.to_string();
let window_id = v.get(3).ok_or(Error::CMParseStr)?.to_string();
let window_index = v.get(4).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::SubscriptionChanged {
name,
session_id,
window_id,
window_index,
})
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_UNLINKED_WINDOW_ADD) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::UnlinkedWindowAdd(window_id))
}
#[cfg(feature = "tmux_3_3")]
s if s.starts_with(NOTIFICATION_UNLINKED_WINDOW_CLOSE) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::UnlinkedWindowClose(window_id))
}
#[cfg(feature = "tmux_3_3")]
s if s.starts_with(NOTIFICATION_UNLINKED_WINDOW_RENAMED) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::UnlinkedWindowRenamed(window_id))
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_WINDOW_ADD) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::WindowAdd(window_id))
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_WINDOW_CLOSE) => {
let v: Vec<_> = s.splitn(2, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::WindowClose(window_id))
}
#[cfg(feature = "tmux_2_5")]
s if s.starts_with(NOTIFICATION_WINDOW_PANE_CHANGED) => {
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let pane_id = v.get(2).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::WindowPaneChanged { window_id, pane_id })
}
#[cfg(feature = "tmux_1_8")]
s if s.starts_with(NOTIFICATION_WINDOW_RENAMED) => {
let v: Vec<_> = s.splitn(3, CONTROL_MODE_SEPARATOR).collect();
let window_id = v.get(1).ok_or(Error::CMParseStr)?.to_string();
let name = v.get(2).ok_or(Error::CMParseStr)?.to_string();
Ok(Response::WindowRenamed { window_id, name })
}
#[cfg(feature = "tmux_1_8")]
_ => Ok(Response::OutputBlockData(self.to_string())),
}
}
}