use serde::{Deserialize, Serialize};
use uuid::Uuid;
use super::dynamic::ProfileSource;
pub type ProfileId = Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum TmuxConnectionMode {
#[default]
ControlMode,
Normal,
}
impl TmuxConnectionMode {
pub fn is_control_mode(v: &Self) -> bool {
*v == Self::ControlMode
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Profile {
pub id: ProfileId,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub working_directory: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shell: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub login_shell: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub command_args: Option<Vec<String>>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tab_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub icon: Option<String>,
#[serde(default)]
pub order: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub parent_id: Option<ProfileId>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub keyboard_shortcut: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub hostname_patterns: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tmux_session_patterns: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tmux_session_name: Option<String>,
#[serde(default, skip_serializing_if = "TmuxConnectionMode::is_control_mode")]
pub tmux_connection_mode: TmuxConnectionMode,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub directory_patterns: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_text: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_color: Option<[u8; 3]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_color_alpha: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_font: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_font_bold: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_top_margin: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_right_margin: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_max_width: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub badge_max_height: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shader: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shader_brightness: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shader_text_opacity: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shader_animation_speed: Option<f32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub shader_texture_set: Option<[Option<String>; 4]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_host: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_user: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_port: Option<u16>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_identity_file: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ssh_extra_args: Option<String>,
#[serde(skip)]
pub source: ProfileSource,
}
impl Profile {
pub fn new(name: impl Into<String>) -> Self {
Self {
id: Uuid::new_v4(),
name: name.into(),
working_directory: None,
shell: None,
login_shell: None,
command: None,
command_args: None,
tab_name: None,
icon: None,
order: 0,
tags: Vec::new(),
parent_id: None,
keyboard_shortcut: None,
hostname_patterns: Vec::new(),
tmux_session_patterns: Vec::new(),
tmux_session_name: None,
tmux_connection_mode: TmuxConnectionMode::default(),
directory_patterns: Vec::new(),
badge_text: None,
badge_color: None,
badge_color_alpha: None,
badge_font: None,
badge_font_bold: None,
badge_top_margin: None,
badge_right_margin: None,
badge_max_width: None,
badge_max_height: None,
shader: None,
shader_brightness: None,
shader_text_opacity: None,
shader_animation_speed: None,
shader_texture_set: None,
ssh_host: None,
ssh_user: None,
ssh_port: None,
ssh_identity_file: None,
ssh_extra_args: None,
source: ProfileSource::default(),
}
}
pub fn with_id(id: ProfileId, name: impl Into<String>) -> Self {
Self {
id,
name: name.into(),
working_directory: None,
shell: None,
login_shell: None,
command: None,
command_args: None,
tab_name: None,
icon: None,
order: 0,
tags: Vec::new(),
parent_id: None,
keyboard_shortcut: None,
hostname_patterns: Vec::new(),
tmux_session_patterns: Vec::new(),
tmux_session_name: None,
tmux_connection_mode: TmuxConnectionMode::default(),
directory_patterns: Vec::new(),
badge_text: None,
badge_color: None,
badge_color_alpha: None,
badge_font: None,
badge_font_bold: None,
badge_top_margin: None,
badge_right_margin: None,
badge_max_width: None,
badge_max_height: None,
shader: None,
shader_brightness: None,
shader_text_opacity: None,
shader_animation_speed: None,
shader_texture_set: None,
ssh_host: None,
ssh_user: None,
ssh_port: None,
ssh_identity_file: None,
ssh_extra_args: None,
source: ProfileSource::default(),
}
}
pub fn working_directory(mut self, dir: impl Into<String>) -> Self {
self.working_directory = Some(dir.into());
self
}
pub fn shell(mut self, shell: impl Into<String>) -> Self {
self.shell = Some(shell.into());
self
}
pub fn login_shell(mut self, login: bool) -> Self {
self.login_shell = Some(login);
self
}
pub fn command(mut self, cmd: impl Into<String>) -> Self {
self.command = Some(cmd.into());
self
}
pub fn command_args(mut self, args: Vec<String>) -> Self {
self.command_args = Some(args);
self
}
pub fn tab_name(mut self, name: impl Into<String>) -> Self {
self.tab_name = Some(name.into());
self
}
pub fn icon(mut self, icon: impl Into<String>) -> Self {
self.icon = Some(icon.into());
self
}
pub fn order(mut self, order: usize) -> Self {
self.order = order;
self
}
pub fn tags(mut self, tags: Vec<String>) -> Self {
self.tags = tags;
self
}
pub fn parent_id(mut self, parent_id: ProfileId) -> Self {
self.parent_id = Some(parent_id);
self
}
pub fn keyboard_shortcut(mut self, shortcut: impl Into<String>) -> Self {
self.keyboard_shortcut = Some(shortcut.into());
self
}
pub fn hostname_patterns(mut self, patterns: Vec<String>) -> Self {
self.hostname_patterns = patterns;
self
}
pub fn tmux_session_patterns(mut self, patterns: Vec<String>) -> Self {
self.tmux_session_patterns = patterns;
self
}
pub fn tmux_session_name(mut self, name: impl Into<String>) -> Self {
self.tmux_session_name = Some(name.into());
self
}
pub fn tmux_connection_mode(mut self, mode: TmuxConnectionMode) -> Self {
self.tmux_connection_mode = mode;
self
}
pub fn directory_patterns(mut self, patterns: Vec<String>) -> Self {
self.directory_patterns = patterns;
self
}
pub fn badge_text(mut self, text: impl Into<String>) -> Self {
self.badge_text = Some(text.into());
self
}
pub fn badge_color(mut self, color: [u8; 3]) -> Self {
self.badge_color = Some(color);
self
}
pub fn badge_color_alpha(mut self, alpha: f32) -> Self {
self.badge_color_alpha = Some(alpha);
self
}
pub fn badge_font(mut self, font: impl Into<String>) -> Self {
self.badge_font = Some(font.into());
self
}
pub fn badge_font_bold(mut self, bold: bool) -> Self {
self.badge_font_bold = Some(bold);
self
}
pub fn badge_top_margin(mut self, margin: f32) -> Self {
self.badge_top_margin = Some(margin);
self
}
pub fn badge_right_margin(mut self, margin: f32) -> Self {
self.badge_right_margin = Some(margin);
self
}
pub fn badge_max_width(mut self, width: f32) -> Self {
self.badge_max_width = Some(width);
self
}
pub fn badge_max_height(mut self, height: f32) -> Self {
self.badge_max_height = Some(height);
self
}
pub fn ssh_host(mut self, host: impl Into<String>) -> Self {
self.ssh_host = Some(host.into());
self
}
pub fn ssh_user(mut self, user: impl Into<String>) -> Self {
self.ssh_user = Some(user.into());
self
}
pub fn ssh_port(mut self, port: u16) -> Self {
self.ssh_port = Some(port);
self
}
pub fn ssh_command_args(&self) -> Option<Vec<String>> {
let host = self.ssh_host.as_ref()?;
let mut args = Vec::new();
if let Some(port) = self.ssh_port
&& port != 22
{
args.push("-p".to_string());
args.push(port.to_string());
}
if let Some(ref identity) = self.ssh_identity_file {
args.push("-i".to_string());
args.push(identity.clone());
}
if let Some(ref extra) = self.ssh_extra_args {
args.extend(extra.split_whitespace().map(String::from));
}
let target = if let Some(ref user) = self.ssh_user {
format!("{}@{}", user, host)
} else {
host.clone()
};
args.push(target);
Some(args)
}
pub fn display_label(&self) -> String {
if let Some(icon) = &self.icon {
format!("{} {}", icon, self.name)
} else {
self.name.clone()
}
}
pub fn validate(&self) -> Vec<String> {
let mut warnings = Vec::new();
if self.name.trim().is_empty() {
warnings.push("Profile name is empty".to_string());
}
if let Some(dir) = &self.working_directory
&& !dir.is_empty()
&& !std::path::Path::new(dir).exists()
{
warnings.push(format!("Working directory does not exist: {}", dir));
}
warnings
}
}
impl Default for Profile {
fn default() -> Self {
Self::new("New Profile")
}
}