use crate::model::muxbox::MuxBox;
use crate::pty_manager::PtyManager;
pub struct StatusIndicator {
pub indicator_type: StatusType,
pub process_info: Option<String>,
pub custom_text: Option<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub enum StatusType {
PtyNormal,
PtyDead,
PtyError,
ScriptRunning,
ScriptCompleted,
ScriptFailed,
Custom(String),
None,
}
impl StatusType {
pub fn get_indicator(&self) -> &str {
match self {
StatusType::PtyNormal => "⚡",
StatusType::PtyDead => "💀",
StatusType::PtyError => "⚠️",
StatusType::ScriptRunning => "▶️",
StatusType::ScriptCompleted => "✅",
StatusType::ScriptFailed => "❌",
StatusType::Custom(indicator) => indicator,
StatusType::None => "",
}
}
pub fn is_visible(&self) -> bool {
!matches!(self, StatusType::None)
}
}
impl StatusIndicator {
pub fn from_muxbox(muxbox: &MuxBox, pty_manager: Option<&PtyManager>) -> Self {
if muxbox.execution_mode.is_pty() {
let indicator_type = if let Some(pty_manager) = pty_manager {
if pty_manager.is_pty_dead(&muxbox.id) {
StatusType::PtyDead
} else if pty_manager.is_pty_in_error_state(&muxbox.id) {
StatusType::PtyError
} else {
StatusType::PtyNormal
}
} else {
StatusType::PtyNormal
};
let process_info = pty_manager.and_then(|pm| pm.get_process_status_summary(&muxbox.id));
Self {
indicator_type,
process_info,
custom_text: None,
}
} else {
Self {
indicator_type: StatusType::None,
process_info: None,
custom_text: None,
}
}
}
pub fn new_custom(indicator: String, text: Option<String>) -> Self {
Self {
indicator_type: StatusType::Custom(indicator),
process_info: None,
custom_text: text,
}
}
pub fn new(status_type: StatusType) -> Self {
Self {
indicator_type: status_type,
process_info: None,
custom_text: None,
}
}
pub fn with_process_info(mut self, info: String) -> Self {
self.process_info = Some(info);
self
}
pub fn with_custom_text(mut self, text: String) -> Self {
self.custom_text = Some(text);
self
}
pub fn render_for_title(&self, original_title: Option<&str>) -> Option<String> {
if !self.indicator_type.is_visible() {
return original_title.map(|s| s.to_string());
}
let mut title_parts = vec![self.indicator_type.get_indicator().to_string()];
if let Some(ref info) = self.process_info {
title_parts.push(format!("[{}]", info));
}
if let Some(ref text) = self.custom_text {
title_parts.push(text.clone());
}
if let Some(title) = original_title {
title_parts.push(title.to_string());
} else if matches!(
self.indicator_type,
StatusType::PtyNormal | StatusType::PtyDead | StatusType::PtyError
) {
title_parts.push("PTY".to_string());
}
Some(title_parts.join(" "))
}
pub fn get_indicator(&self) -> &str {
self.indicator_type.get_indicator()
}
pub fn is_visible(&self) -> bool {
self.indicator_type.is_visible()
}
pub fn get_full_status(&self) -> String {
if !self.is_visible() {
return String::new();
}
let mut parts = vec![self.get_indicator().to_string()];
if let Some(ref info) = self.process_info {
parts.push(format!("[{}]", info));
}
if let Some(ref text) = self.custom_text {
parts.push(text.clone());
}
parts.join(" ")
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::model::common::ExecutionMode;
#[test]
fn test_status_type_indicators() {
assert_eq!(StatusType::PtyNormal.get_indicator(), "⚡");
assert_eq!(StatusType::PtyDead.get_indicator(), "💀");
assert_eq!(StatusType::PtyError.get_indicator(), "⚠️");
assert_eq!(StatusType::ScriptRunning.get_indicator(), "▶️");
assert_eq!(StatusType::ScriptCompleted.get_indicator(), "✅");
assert_eq!(StatusType::ScriptFailed.get_indicator(), "❌");
assert_eq!(StatusType::Custom("🚀".to_string()).get_indicator(), "🚀");
assert_eq!(StatusType::None.get_indicator(), "");
}
#[test]
fn test_status_type_visibility() {
assert!(StatusType::PtyNormal.is_visible());
assert!(StatusType::PtyDead.is_visible());
assert!(StatusType::PtyError.is_visible());
assert!(StatusType::Custom("⭐".to_string()).is_visible());
assert!(!StatusType::None.is_visible());
}
#[test]
fn test_custom_status_indicator() {
let indicator =
StatusIndicator::new_custom("🎯".to_string(), Some("Custom Status".to_string()));
assert_eq!(indicator.get_indicator(), "🎯");
assert_eq!(indicator.custom_text, Some("Custom Status".to_string()));
assert!(indicator.is_visible());
}
#[test]
fn test_status_indicator_with_process_info() {
let indicator =
StatusIndicator::new(StatusType::PtyNormal).with_process_info("bash:1234".to_string());
assert_eq!(indicator.get_indicator(), "⚡");
assert_eq!(indicator.process_info, Some("bash:1234".to_string()));
}
#[test]
fn test_render_for_title() {
let indicator =
StatusIndicator::new(StatusType::PtyNormal).with_process_info("bash:1234".to_string());
let result = indicator.render_for_title(Some("My Terminal"));
assert_eq!(result, Some("⚡ [bash:1234] My Terminal".to_string()));
}
#[test]
fn test_render_for_title_no_original() {
let indicator =
StatusIndicator::new(StatusType::PtyNormal).with_process_info("bash:1234".to_string());
let result = indicator.render_for_title(None);
assert_eq!(result, Some("⚡ [bash:1234] PTY".to_string()));
}
#[test]
fn test_render_for_title_none_status() {
let indicator = StatusIndicator::new(StatusType::None);
let result = indicator.render_for_title(Some("Regular Title"));
assert_eq!(result, Some("Regular Title".to_string()));
}
#[test]
fn test_get_full_status() {
let indicator = StatusIndicator::new(StatusType::PtyError)
.with_process_info("failed_cmd".to_string())
.with_custom_text("Connection Lost".to_string());
let result = indicator.get_full_status();
assert_eq!(result, "⚠️ [failed_cmd] Connection Lost");
}
#[test]
fn test_from_muxbox_pty() {
let mut muxbox = MuxBox::default();
muxbox.id = "test_pty".to_string();
muxbox.execution_mode = ExecutionMode::Pty;
let indicator = StatusIndicator::from_muxbox(&muxbox, None);
assert!(matches!(indicator.indicator_type, StatusType::PtyNormal));
assert_eq!(indicator.get_indicator(), "⚡");
}
#[test]
fn test_from_muxbox_non_pty() {
let mut muxbox = MuxBox::default();
muxbox.execution_mode = ExecutionMode::Thread;
let indicator = StatusIndicator::from_muxbox(&muxbox, None);
assert!(matches!(indicator.indicator_type, StatusType::None));
assert!(!indicator.is_visible());
}
}