use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ScriptEvent {
pub kind: String,
pub data: ScriptEventData,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "data_type")]
pub enum ScriptEventData {
Empty {},
CwdChanged {
cwd: String,
},
CommandComplete {
command: String,
exit_code: Option<i32>,
},
TitleChanged {
title: String,
},
SizeChanged {
cols: usize,
rows: usize,
},
VariableChanged {
name: String,
value: String,
old_value: Option<String>,
},
EnvironmentChanged {
key: String,
value: String,
old_value: Option<String>,
},
BadgeChanged {
text: Option<String>,
},
TriggerMatched {
pattern: String,
matched_text: String,
line: usize,
},
ZoneEvent {
zone_id: u64,
zone_type: String,
event: String,
},
Generic {
fields: HashMap<String, serde_json::Value>,
},
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(tag = "type")]
pub enum ScriptCommand {
WriteText {
text: String,
},
Notify {
title: String,
body: String,
},
SetBadge {
text: String,
},
SetVariable {
name: String,
value: String,
},
RunCommand {
command: String,
},
ChangeConfig {
key: String,
value: serde_json::Value,
},
Log {
level: String,
message: String,
},
SetPanel {
title: String,
content: String,
},
ClearPanel {},
}
pub fn strip_vt_sequences(text: &str) -> String {
let mut result = String::with_capacity(text.len());
let mut chars = text.chars().peekable();
while let Some(c) = chars.next() {
if c != '\x1b' {
result.push(c);
continue;
}
match chars.peek().copied() {
Some('[') => {
chars.next(); while let Some(&ch) = chars.peek() {
chars.next();
if ch.is_ascii_alphabetic() || ch == '@' || ch == '`' {
break;
}
}
}
Some(']') => {
chars.next(); while let Some(ch) = chars.next() {
if ch == '\x07' {
break;
}
if ch == '\x1b' && chars.peek() == Some(&'\\') {
chars.next();
break;
}
}
}
Some('P') | Some('_') | Some('^') | Some('X') => {
chars.next(); while let Some(ch) = chars.next() {
if ch == '\x1b' && chars.peek() == Some(&'\\') {
chars.next();
break;
}
}
}
Some('(') | Some(')') | Some('*') | Some('+') => {
chars.next();
chars.next();
}
Some(_) => {
chars.next();
}
None => {}
}
}
result
}
impl ScriptCommand {
pub fn requires_permission(&self) -> bool {
matches!(
self,
ScriptCommand::RunCommand { .. }
| ScriptCommand::WriteText { .. }
| ScriptCommand::ChangeConfig { .. }
)
}
pub fn permission_flag_name(&self) -> Option<&'static str> {
match self {
ScriptCommand::RunCommand { .. } => Some("allow_run_command"),
ScriptCommand::WriteText { .. } => Some("allow_write_text"),
ScriptCommand::ChangeConfig { .. } => Some("allow_change_config"),
_ => None,
}
}
pub fn is_rate_limited(&self) -> bool {
matches!(
self,
ScriptCommand::RunCommand { .. } | ScriptCommand::WriteText { .. }
)
}
pub fn command_name(&self) -> &'static str {
match self {
ScriptCommand::WriteText { .. } => "WriteText",
ScriptCommand::Notify { .. } => "Notify",
ScriptCommand::SetBadge { .. } => "SetBadge",
ScriptCommand::SetVariable { .. } => "SetVariable",
ScriptCommand::RunCommand { .. } => "RunCommand",
ScriptCommand::ChangeConfig { .. } => "ChangeConfig",
ScriptCommand::Log { .. } => "Log",
ScriptCommand::SetPanel { .. } => "SetPanel",
ScriptCommand::ClearPanel {} => "ClearPanel",
}
}
}