- model/ 按领域拆为 connection/subscription/message/publish/variable/log/settings - mqtt/ 拆为门面(引擎与事件)、tls、v5、v4 四块 - update/ 拆为门面(版本工具与命名约定)、check(检查)、install(下载/安装/清理) - sysmon 只保留进程资源采样,窗口置顶迁至新 platform.rs - 对外路径经 mod.rs 再导出保持不变,测试随门面保留(33 通过)
72 lines
2.8 KiB
Rust
72 lines
2.8 KiB
Rust
//! 全局变量:模板占位符渲染(含 {{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));
|
||
}
|
||
}
|
||
|
||
// ─── 日志 ────────────────────────────────────────────────────────────────────
|