Files
mqttx/gpui-app/src/model/variable.rs
T
JoeAllen b5089f5898 refactor(core): 拆分 model/mqtt/update 为单一职责子模块
- model/ 按领域拆为 connection/subscription/message/publish/variable/log/settings
- mqtt/ 拆为门面(引擎与事件)、tls、v5、v4 四块
- update/ 拆为门面(版本工具与命名约定)、check(检查)、install(下载/安装/清理)
- sysmon 只保留进程资源采样,窗口置顶迁至新 platform.rs
- 对外路径经 mod.rs 再导出保持不变,测试随门面保留(33 通过)
2026-09-29 22:20:53 +08:00

72 lines
2.8 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
//! 全局变量:模板占位符渲染(含 {{timestamp}} 时间基一致性)。
use serde::{Deserialize, Serialize};
use super::*;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GlobalVariable {
pub key: String,
pub value: String,
}
/// 解析 `{{key}}` 模板:
/// - 用户定义的全局变量优先;
/// - 内置变量:`{{$ts}}`(unix 秒)、`{{$ts_ms}}`(毫秒)、`{{$uuid}}`;
/// - 找不到的占位符保持原样。
pub fn render_template(input: &str, vars: &[GlobalVariable]) -> String {
let mut out = String::with_capacity(input.len());
let bytes = input.as_bytes();
// 同一次渲染内时间基准保持一致,避免一条消息里多个时间变量取值不同。
let now_ms = chrono::Local::now().timestamp_millis();
let now_s = now_ms / 1000;
let mut i = 0;
while i < bytes.len() {
if i + 1 < bytes.len() && bytes[i] == b'{' && bytes[i + 1] == b'{'
&& let Some(rel_end) = input[i + 2..].find("}}") {
let key = &input[i + 2..i + 2 + rel_end];
let resolved = match key.trim() {
"$ts" => Some(now_s.to_string()),
"$ts_ms" => Some(now_ms.to_string()),
"$uuid" => Some(uuid::Uuid::new_v4().to_string()),
k => vars
.iter()
.find(|v| v.key == k)
.map(|v| v.value.clone()),
};
match resolved {
Some(v) => out.push_str(&v),
None => out.push_str(&input[i..i + 2 + rel_end + 2]),
}
i = i + 2 + rel_end + 2;
continue;
}
// 按字符推进,避免把多字节 UTF-8 拆开
let ch = input[i..].chars().next().unwrap();
out.push(ch);
i += ch.len_utf8();
}
out
}
/// 连接前渲染遗嘱消息中的 `{{变量}}`。
/// 只作用于本次连接的配置副本,落盘的连接配置保持模板原文,
/// 这样改全局变量后下次重连即生效,`$ts` 等也按连接时刻求值。
pub fn render_will_templates(cfg: &mut ConnectionConfig, vars: &[GlobalVariable]) {
let Some(will) = cfg.last_will.as_mut() else {
return;
};
will.topic = render_template(&will.topic, vars);
will.payload = render_template(&will.payload, vars);
if let Some(v) = will.content_type.take() {
will.content_type = Some(render_template(&v, vars));
}
if let Some(v) = will.response_topic.take() {
will.response_topic = Some(render_template(&v, vars));
}
}
// ─── 日志 ────────────────────────────────────────────────────────────────────