Skip to main content

zerolaunch_plugin_api/plugin/
trigger.rs

1//! 关键词触发判定 —— 宿主侧关键词路由与插件侧默认实现的**唯一**语义定义处。
2//!
3//! 使用范围:宿主插件路由(`SessionDispatcher` 的关键词兜底路径)与内置/第三方插件的
4//! `Plugin::match_query` 默认语义。放在 plugin-api 是为了让宿主与 SDK 两侧共用同一份
5//! 判定实现,避免"宿主一份、插件一份"的语义漂移。
6
7/// 关键词触发判定(纯函数,无 IO)。
8///
9/// 语义:输入按首个空格切分,首词小写后必须精确命中 `keywords` 中的某一项(同样按小写比较),
10/// 且必须存在剩余内容(单独的触发词不算命中——`"fy"` 不命中,`"fy hello"` 命中)。
11///
12/// 参数:
13/// - `keywords` - 插件声明的触发词列表(大小写不敏感)。
14/// - `raw_query` - 用户输入的原始查询串。
15///
16/// 返回:命中时返回剥离触发词后的剩余输入(`"fy hello"` → `"hello"`);未命中返回 `None`。
17pub fn keyword_trigger_match<'a>(keywords: &[String], raw_query: &'a str) -> Option<&'a str> {
18    let mut parts = raw_query.splitn(2, ' ');
19    let first = parts.next().unwrap_or("");
20    let rest = parts.next()?;
21    let first_lower = first.to_lowercase();
22    keywords
23        .iter()
24        .any(|kw| kw.to_lowercase() == first_lower)
25        .then_some(rest)
26}
27
28#[cfg(test)]
29mod tests {
30    use super::keyword_trigger_match;
31
32    /// 触发词必须带空格分隔:`"fy hello"` 命中,单独的 `"fy"` 与粘连的 `"fyhello"` 不命中。
33    #[test]
34    fn requires_space_separated_trigger() {
35        let keywords = vec!["fy".to_string()];
36        assert_eq!(keyword_trigger_match(&keywords, "fy hello"), Some("hello"));
37        assert_eq!(keyword_trigger_match(&keywords, "fy"), None);
38        assert_eq!(keyword_trigger_match(&keywords, "fyhello"), None);
39        assert_eq!(keyword_trigger_match(&keywords, "xx hello"), None);
40    }
41
42    /// 大小写不敏感:声明 `Translate` 时 `translate xxx` 与 `TRANSLATE xxx` 均命中。
43    #[test]
44    fn case_insensitive() {
45        let keywords = vec!["Translate".to_string()];
46        assert_eq!(keyword_trigger_match(&keywords, "translate hi"), Some("hi"));
47        assert_eq!(keyword_trigger_match(&keywords, "TRANSLATE hi"), Some("hi"));
48    }
49
50    /// 无触发词声明时恒不命中(未声明触发词的行内插件不参与关键词路由)。
51    #[test]
52    fn empty_keywords_never_match() {
53        assert_eq!(keyword_trigger_match(&[], "fy hello"), None);
54    }
55}