use std::fmt;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
pub use crate::identity::SessionName;
use crate::RmuxError;
pub use rmux_types::TerminalSize;
#[path = "types/hooks.rs"]
mod hooks;
#[path = "types/options.rs"]
mod options;
pub use hooks::{HookLifecycle, HookName};
pub use options::{OptionName, SetOptionMode};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct PaneOutputSubscriptionId(u64);
impl PaneOutputSubscriptionId {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SdkWaitOwnerId(u64);
impl SdkWaitOwnerId {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct SdkWaitId(u64);
impl SdkWaitId {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn as_u64(self) -> u64 {
self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Target {
Session(SessionName),
Window(WindowTarget),
Pane(PaneTarget),
}
impl Target {
pub fn parse(value: &str) -> Result<Self, RmuxError> {
if let Some((session_name, tail)) = value.split_once(':') {
let session_name = SessionName::new(session_name.to_owned())?;
if !tail.is_empty() && tail.chars().all(|character| character.is_ascii_digit()) {
let window_index = parse_window_index(value, tail)?;
return Ok(Self::Window(WindowTarget::with_window(
session_name,
window_index,
)));
}
if let Some((window_index, pane_index)) = tail.split_once('.') {
let window_index = parse_window_index(value, window_index)?;
let pane_index = parse_pane_index(value, pane_index)?;
return Ok(Self::Pane(PaneTarget::with_window(
session_name,
window_index,
pane_index,
)));
}
return Err(RmuxError::invalid_target(
value,
"targets must match 'session', 'session:window', or 'session:window.pane'",
));
}
Ok(Self::Session(SessionName::new(value.to_owned())?))
}
#[must_use]
pub fn session_name(&self) -> &SessionName {
match self {
Self::Session(session_name) => session_name,
Self::Window(target) => target.session_name(),
Self::Pane(target) => target.session_name(),
}
}
}
impl fmt::Display for Target {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Session(session_name) => session_name.fmt(formatter),
Self::Window(target) => target.fmt(formatter),
Self::Pane(target) => target.fmt(formatter),
}
}
}
impl FromStr for Target {
type Err = RmuxError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
Self::parse(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct WindowTarget {
session_name: SessionName,
window_index: u32,
}
impl WindowTarget {
#[must_use]
pub const fn new(session_name: SessionName) -> Self {
Self::with_window(session_name, 0)
}
#[must_use]
pub const fn with_window(session_name: SessionName, window_index: u32) -> Self {
Self {
session_name,
window_index,
}
}
#[must_use]
pub const fn session_name(&self) -> &SessionName {
&self.session_name
}
#[must_use]
pub const fn window_index(&self) -> u32 {
self.window_index
}
}
impl fmt::Display for WindowTarget {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(formatter, "{}:{}", self.session_name, self.window_index)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PaneTarget {
session_name: SessionName,
window_index: u32,
pane_index: u32,
}
impl PaneTarget {
#[must_use]
pub const fn new(session_name: SessionName, pane_index: u32) -> Self {
Self::with_window(session_name, 0, pane_index)
}
#[must_use]
pub const fn with_window(
session_name: SessionName,
window_index: u32,
pane_index: u32,
) -> Self {
Self {
session_name,
window_index,
pane_index,
}
}
#[must_use]
pub const fn session_name(&self) -> &SessionName {
&self.session_name
}
#[must_use]
pub const fn window_index(&self) -> u32 {
self.window_index
}
#[must_use]
pub const fn pane_index(&self) -> u32 {
self.pane_index
}
}
impl fmt::Display for PaneTarget {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
formatter,
"{}:{}.{}",
self.session_name, self.window_index, self.pane_index
)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ScopeSelector {
Global,
Session(SessionName),
Window(WindowTarget),
Pane(PaneTarget),
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum OptionScopeSelector {
ServerGlobal,
SessionGlobal,
WindowGlobal,
Session(SessionName),
Window(WindowTarget),
Pane(PaneTarget),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum LayoutName {
MainVertical,
MainHorizontal,
EvenHorizontal,
EvenVertical,
Tiled,
MainHorizontalMirrored,
MainVerticalMirrored,
}
impl fmt::Display for LayoutName {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::MainVertical => formatter.write_str("main-vertical"),
Self::MainHorizontal => formatter.write_str("main-horizontal"),
Self::EvenHorizontal => formatter.write_str("even-horizontal"),
Self::EvenVertical => formatter.write_str("even-vertical"),
Self::Tiled => formatter.write_str("tiled"),
Self::MainHorizontalMirrored => formatter.write_str("main-horizontal-mirrored"),
Self::MainVerticalMirrored => formatter.write_str("main-vertical-mirrored"),
}
}
}
impl FromStr for LayoutName {
type Err = RmuxError;
fn from_str(value: &str) -> Result<Self, Self::Err> {
match value {
"main-vertical" => Ok(Self::MainVertical),
"main-horizontal" => Ok(Self::MainHorizontal),
"even-horizontal" => Ok(Self::EvenHorizontal),
"even-vertical" => Ok(Self::EvenVertical),
"tiled" => Ok(Self::Tiled),
"main-horizontal-mirrored" => Ok(Self::MainHorizontalMirrored),
"main-vertical-mirrored" => Ok(Self::MainVerticalMirrored),
_ => Err(RmuxError::Server(format!("unknown layout: {value}"))),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum SplitDirection {
#[default]
Vertical,
Horizontal,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum ResizePaneAdjustment {
AbsoluteWidth {
columns: u16,
},
AbsoluteHeight {
rows: u16,
},
Zoom,
Up {
cells: u16,
},
Down {
cells: u16,
},
Left {
cells: u16,
},
Right {
cells: u16,
},
NoOp,
}
fn parse_pane_index(target: &str, pane_index: &str) -> Result<u32, RmuxError> {
if pane_index.is_empty() {
return Err(RmuxError::invalid_target(
target,
"pane index must be an unsigned integer",
));
}
pane_index
.parse::<u32>()
.map_err(|_| RmuxError::invalid_target(target, "pane index must be an unsigned integer"))
}
fn parse_window_index(target: &str, window_index: &str) -> Result<u32, RmuxError> {
if window_index.is_empty() {
return Err(RmuxError::invalid_target(
target,
"window index must be an unsigned integer",
));
}
window_index
.parse::<u32>()
.map_err(|_| RmuxError::invalid_target(target, "window index must be an unsigned integer"))
}
#[cfg(test)]
mod tests;