use std::error::Error as StdError;
use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ClientContext {
Outside,
Nested,
}
impl ClientContext {
#[must_use]
pub const fn is_nested(self) -> bool {
matches!(self, Self::Nested)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct NestedContextError;
impl fmt::Display for NestedContextError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("switch-client requires a nested client context")
}
}
impl StdError for NestedContextError {}
#[must_use]
pub fn detect_context() -> ClientContext {
detect_context_from_env(std::env::var_os("RMUX").as_deref())
}
pub fn ensure_nested_context(context: ClientContext) -> Result<(), NestedContextError> {
if context.is_nested() {
Ok(())
} else {
Err(NestedContextError)
}
}
pub fn require_nested_context() -> Result<(), NestedContextError> {
ensure_nested_context(detect_context())
}
fn detect_context_from_env(tmux_value: Option<&std::ffi::OsStr>) -> ClientContext {
match tmux_value {
Some(value) if !value.is_empty() => ClientContext::Nested,
_ => ClientContext::Outside,
}
}
#[cfg(test)]
mod tests {
use super::{
detect_context_from_env, ensure_nested_context, require_nested_context, ClientContext,
NestedContextError,
};
use std::ffi::OsStr;
use std::sync::Mutex;
static RMUX_ENV_LOCK: Mutex<()> = Mutex::new(());
#[test]
fn absent_rmux_is_outside() {
assert_eq!(detect_context_from_env(None), ClientContext::Outside);
}
#[test]
fn empty_rmux_is_outside() {
assert_eq!(
detect_context_from_env(Some(OsStr::new(""))),
ClientContext::Outside
);
}
#[test]
fn nonempty_rmux_is_nested() {
assert_eq!(
detect_context_from_env(Some(OsStr::new("/tmp/rmux-1000/default,12345,0"))),
ClientContext::Nested
);
}
#[test]
fn any_nonempty_value_is_nested() {
assert_eq!(
detect_context_from_env(Some(OsStr::new("x"))),
ClientContext::Nested
);
}
#[test]
fn is_nested_accessor() {
assert!(ClientContext::Nested.is_nested());
assert!(!ClientContext::Outside.is_nested());
}
#[test]
fn ensure_nested_context_rejects_outside_contexts() {
assert_eq!(
ensure_nested_context(ClientContext::Outside),
Err(NestedContextError)
);
assert_eq!(ensure_nested_context(ClientContext::Nested), Ok(()));
}
#[test]
fn require_nested_context_reads_env() {
let _guard = RMUX_ENV_LOCK.lock().expect("rmux env lock");
let original = std::env::var_os("RMUX");
std::env::remove_var("RMUX");
assert_eq!(super::detect_context(), ClientContext::Outside);
assert_eq!(require_nested_context(), Err(NestedContextError));
std::env::set_var("RMUX", "/tmp/rmux-1000/default,1,0");
assert_eq!(super::detect_context(), ClientContext::Nested);
assert_eq!(require_nested_context(), Ok(()));
match original {
Some(value) => std::env::set_var("RMUX", value),
None => std::env::remove_var("RMUX"),
}
}
}