use std::str::FromStr;
use nom::{Parser, character::complete::char, combinator::all_consuming};
use serde::{Deserialize, Serialize};
use smol::process::Command;
use crate::{
Result,
error::{Error, map_add_intent},
parse::{quoted_nonempty_string, quoted_string},
};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Client {
pub session_name: String,
pub last_session_name: String,
}
impl FromStr for Client {
type Err = Error;
fn from_str(input: &str) -> std::result::Result<Self, Self::Err> {
let desc = "Client";
let intent = "'##{client_session}':'##{client_last_session}'";
let parser = (quoted_nonempty_string, char(':'), quoted_string);
let (_, (session_name, _, last_session_name)) = all_consuming(parser)
.parse(input)
.map_err(|e| map_add_intent(desc, intent, e))?;
Ok(Client {
session_name: session_name.to_string(),
last_session_name: last_session_name.to_string(),
})
}
}
pub async fn current() -> Result<Client> {
let args = vec![
"display-message",
"-p",
"-F",
"'#{client_session}':'#{client_last_session}'",
];
let output = Command::new("tmux").args(&args).output().await?;
let buffer = String::from_utf8(output.stdout)?;
Client::from_str(buffer.trim_end())
}
pub fn display_message(message: &str) {
let args = vec!["display-message", message];
std::process::Command::new("tmux")
.args(&args)
.output()
.expect("Cannot communicate with Tmux for displaying message");
}
pub async fn switch_client(session_name: &str) -> Result<()> {
let exact_session_name = format!("={session_name}");
let args = vec!["switch-client", "-t", &exact_session_name];
Command::new("tmux")
.args(&args)
.output()
.await
.expect("Cannot communicate with Tmux for switching the client");
Ok(())
}
#[cfg(test)]
mod tests {
use super::Client;
use std::str::FromStr;
#[test]
fn parse_client_with_both_sessions() {
let input = "'current-session':'last-session'";
let client = Client::from_str(input).expect("Should parse valid client");
assert_eq!(client.session_name, "current-session");
assert_eq!(client.last_session_name, "last-session");
}
#[test]
fn parse_client_with_empty_last_session() {
let input = "'my-session':''";
let client = Client::from_str(input).expect("Should parse client with empty last session");
assert_eq!(client.session_name, "my-session");
assert_eq!(client.last_session_name, "");
}
#[test]
fn parse_client_with_special_chars_in_name() {
let input = "'server: $123':'dev-env'";
let client = Client::from_str(input).expect("Should parse client with special chars");
assert_eq!(client.session_name, "server: $123");
assert_eq!(client.last_session_name, "dev-env");
}
#[test]
fn parse_client_fails_on_empty_current_session() {
let input = "'':'last-session'";
let result = Client::from_str(input);
assert!(result.is_err());
}
#[test]
fn parse_client_fails_on_missing_quotes() {
let input = "current-session:last-session";
let result = Client::from_str(input);
assert!(result.is_err());
}
#[test]
fn parse_client_fails_on_missing_colon() {
let input = "'current-session''last-session'";
let result = Client::from_str(input);
assert!(result.is_err());
}
#[test]
fn parse_client_fails_on_extra_content() {
let input = "'current':'last':extra";
let result = Client::from_str(input);
assert!(result.is_err());
}
}