Skip to main content

smcp_computer/mcp_clients/
vrl_runtime.rs

1/*!
2* 文件名: vrl_runtime
3* 作者: JQQ
4* 创建日期: 2025/12/16
5* 最后修改日期: 2025/12/16
6* 版权: 2023 JQQ. All rights reserved.
7* 依赖: vrl, serde_json
8* 描述: VRL运行时实现,用于转换工具调用结果
9*/
10use thiserror::Error;
11
12#[cfg(feature = "vrl")]
13use {
14    serde_json::Value,
15    vrl::{
16        compiler::{self, runtime::Runtime, TargetValue, TimeZone},
17        stdlib,
18        value::{Secrets, Value as VrlValue},
19    },
20};
21
22#[cfg(feature = "vrl")]
23#[derive(Error, Debug)]
24pub enum VrlError {
25    #[error("VRL compilation error: {0}")]
26    Compilation(String),
27    #[error("VRL runtime error: {0}")]
28    Runtime(String),
29    #[error("Invalid timezone: {0}")]
30    InvalidTimezone(String),
31}
32
33#[cfg(feature = "vrl")]
34#[derive(Debug)]
35pub struct VrlResult {
36    pub processed_event: Value,
37}
38
39#[cfg(feature = "vrl")]
40pub struct VrlRuntime {
41    runtime: Runtime,
42}
43
44#[cfg(feature = "vrl")]
45impl VrlRuntime {
46    /// 创建新的VRL运行时
47    pub fn new() -> Self {
48        Self {
49            runtime: Runtime::default(),
50        }
51    }
52
53    /// 检查VRL脚本的语法
54    pub fn check_syntax(script: &str) -> Result<(), VrlError> {
55        match compiler::compile(script, &stdlib::all()) {
56            Ok(_) => Ok(()),
57            Err(e) => Err(VrlError::Compilation(format!("{:?}", e))),
58        }
59    }
60
61    /// 运行VRL脚本
62    pub fn run(
63        &mut self,
64        script: &str,
65        event: Value,
66        timezone: &str,
67    ) -> Result<VrlResult, VrlError> {
68        // 编译VRL脚本
69        let compilation = compiler::compile(script, &stdlib::all())
70            .map_err(|e| VrlError::Compilation(format!("{:?}", e)))?;
71
72        // 转换JSON事件到VRL Value
73        let vrl_value = self.json_to_vrl_value(event)?;
74
75        // 创建TargetValue作为执行目标
76        let mut target = TargetValue {
77            value: vrl_value,
78            metadata: VrlValue::Object(Default::default()),
79            secrets: Secrets::default(),
80        };
81
82        // 解析时区
83        let tz = if timezone == "UTC" {
84            TimeZone::default()
85        } else {
86            // 尝试解析时区字符串
87            TimeZone::parse(timezone).unwrap_or_default()
88        };
89
90        // 执行VRL程序
91        self.runtime
92            .resolve(&mut target, &compilation.program, &tz)
93            .map_err(|e| VrlError::Runtime(format!("{:?}", e)))?;
94
95        // 获取转换后的值
96        let processed = target.value;
97
98        // 转换回JSON
99        let processed_event = self.vrl_value_to_json(processed)?;
100
101        Ok(VrlResult { processed_event })
102    }
103
104    /// 将JSON值转换为VRL值
105    fn json_to_vrl_value(&self, value: Value) -> Result<VrlValue, VrlError> {
106        // VRL的Value类型实现了From<serde_json::Value>
107        Ok(VrlValue::from(value))
108    }
109
110    /// 将VRL值转换为JSON
111    fn vrl_value_to_json(&self, value: VrlValue) -> Result<Value, VrlError> {
112        // VRL的Value类型可以转换为serde_json::Value
113        value
114            .try_into()
115            .map_err(|e| VrlError::Runtime(format!("Failed to convert VRL value: {:?}", e)))
116    }
117}
118
119#[cfg(feature = "vrl")]
120impl Default for VrlRuntime {
121    fn default() -> Self {
122        Self::new()
123    }
124}
125
126// 当没有启用vrl feature时的占位实现
127#[cfg(not(feature = "vrl"))]
128#[derive(Error, Debug)]
129pub enum VrlError {
130    #[error("VRL support is not enabled")]
131    NotEnabled,
132}
133
134#[cfg(not(feature = "vrl"))]
135#[derive(Debug)]
136pub struct VrlResult {
137    pub processed_event: serde_json::Value,
138}
139
140#[cfg(not(feature = "vrl"))]
141pub struct VrlRuntime;
142
143#[cfg(not(feature = "vrl"))]
144impl VrlRuntime {
145    pub fn new() -> Self {
146        Self
147    }
148
149    pub fn check_syntax(_script: &str) -> Result<(), VrlError> {
150        Err(VrlError::NotEnabled)
151    }
152
153    pub fn run(
154        &self,
155        _script: &str,
156        event: serde_json::Value,
157        _timezone: &str,
158    ) -> Result<VrlResult, VrlError> {
159        // 当VRL未启用时,直接返回原始事件
160        Ok(VrlResult {
161            processed_event: event,
162        })
163    }
164}
165
166#[cfg(not(feature = "vrl"))]
167impl Default for VrlRuntime {
168    fn default() -> Self {
169        Self
170    }
171}
172
173#[cfg(test)]
174mod tests {
175    use super::*;
176    use serde_json::json;
177
178    #[cfg(feature = "vrl")]
179    #[test]
180    fn test_vrl_syntax_check_valid_scripts() {
181        // 表驱动测试有效VRL脚本
182        let valid_cases = vec![
183            (".field = 1", "simple assignment"),
184            (".field = \"value\"", "string assignment"),
185            (".field = true", "boolean assignment"),
186            (".field = .existing", "field reference"),
187        ];
188
189        for (script, description) in valid_cases {
190            assert!(
191                VrlRuntime::check_syntax(script).is_ok(),
192                "Script should be valid: {} - {}",
193                script,
194                description
195            );
196        }
197    }
198
199    #[cfg(feature = "vrl")]
200    #[test]
201    fn test_vrl_syntax_check_invalid_scripts() {
202        // 表驱动测试无效VRL脚本
203        let invalid_cases = vec![
204            (".field =", "incomplete assignment"),
205            ("= 1", "missing target"),
206            (".field .", "invalid syntax"),
207            (".field = now(", "unclosed function"),
208            (".field = . +", "incomplete expression"),
209            ("if .field", "incomplete if statement"),
210        ];
211
212        for (script, description) in invalid_cases {
213            assert!(
214                VrlRuntime::check_syntax(script).is_err(),
215                "Script should be invalid: {} - {}",
216                script,
217                description
218            );
219        }
220    }
221
222    #[cfg(feature = "vrl")]
223    #[test]
224    fn test_vrl_runtime_basic() {
225        let mut runtime = VrlRuntime::new();
226
227        let script = r#"
228            .processed = true
229            .status = "ok"
230        "#;
231
232        let event = json!({
233            "original": "value"
234        });
235
236        let result = runtime.run(script, event, "UTC").unwrap();
237
238        // 简化实现目前返回原始事件
239        assert_eq!(result.processed_event["original"], "value");
240    }
241
242    #[cfg(feature = "vrl")]
243    #[test]
244    fn test_vrl_runtime_complex_event() {
245        let mut runtime = VrlRuntime::new();
246
247        let script = r#"
248            .metadata.transformed = true
249            .count = 3
250        "#;
251
252        let event = json!({
253            "items": [1, 2, 3],
254            "nested": {
255                "value": 42
256            }
257        });
258
259        let result = runtime.run(script, event, "UTC").unwrap();
260
261        // 验证原始数据保持不变
262        assert_eq!(result.processed_event["items"].as_array().unwrap().len(), 3);
263        assert_eq!(result.processed_event["nested"]["value"], 42);
264    }
265
266    #[cfg(feature = "vrl")]
267    #[test]
268    fn test_vrl_runtime_error_handling() {
269        let mut runtime = VrlRuntime::new();
270
271        // 无效脚本
272        let script = ".field =";
273        let event = json!({"test": "value"});
274
275        assert!(runtime.run(script, event, "UTC").is_err());
276    }
277
278    #[cfg(feature = "vrl")]
279    #[test]
280    fn test_json_to_vrl_value_conversion() {
281        let runtime = VrlRuntime::new();
282
283        // 测试各种JSON类型
284        let test_cases = vec![
285            (json!(null), "null"),
286            (json!(true), "boolean"),
287            (json!(42), "number"),
288            (json!("string"), "string"),
289            (json!([1, 2, 3]), "array"),
290            (json!({"key": "value"}), "object"),
291        ];
292
293        for (value, description) in test_cases {
294            let vrl_value = runtime.json_to_vrl_value(value.clone()).unwrap();
295            // 转换回JSON应该保持一致
296            let json_back = runtime.vrl_value_to_json(vrl_value).unwrap();
297            assert_eq!(value, json_back, "Conversion failed for: {}", description);
298        }
299    }
300
301    #[test]
302    fn test_vrl_disabled_feature() {
303        // 当feature未启用时,所有操作应该返回NotEnabled错误或原始值
304        #[cfg(not(feature = "vrl"))]
305        assert!(matches!(
306            VrlRuntime::check_syntax(".field = 1"),
307            Err(VrlError::NotEnabled)
308        ));
309
310        #[cfg(feature = "vrl")]
311        {
312            let mut runtime = VrlRuntime::new();
313            let event = json!({"test": "value"});
314            let result = runtime.run(".field = 1", event.clone(), "UTC").unwrap();
315            // VRL应该添加field字段
316            assert_eq!(result.processed_event["test"], "value");
317            assert_eq!(result.processed_event["field"], 1);
318        }
319    }
320
321    #[cfg(feature = "vrl")]
322    #[test]
323    fn test_vrl_error_display() {
324        let compilation_error = VrlError::Compilation("test error".to_string());
325        assert_eq!(
326            compilation_error.to_string(),
327            "VRL compilation error: test error"
328        );
329
330        let runtime_error = VrlError::Runtime("test runtime error".to_string());
331        assert_eq!(
332            runtime_error.to_string(),
333            "VRL runtime error: test runtime error"
334        );
335
336        let timezone_error = VrlError::InvalidTimezone("UTC+25".to_string());
337        assert_eq!(timezone_error.to_string(), "Invalid timezone: UTC+25");
338    }
339
340    #[cfg(feature = "vrl")]
341    #[test]
342    fn test_vrl_result_debug() {
343        let result = VrlResult {
344            processed_event: json!({"test": "value"}),
345        };
346
347        let debug_str = format!("{:?}", result);
348        assert!(debug_str.contains("VrlResult"));
349        assert!(debug_str.contains("processed_event"));
350    }
351
352    #[cfg(feature = "vrl")]
353    #[test]
354    fn test_vrl_runtime_default() {
355        assert!(VrlRuntime::check_syntax(".field = 1").is_ok());
356    }
357
358    #[cfg(feature = "vrl")]
359    #[tokio::test]
360    async fn test_vrl_async_context() {
361        // 测试在异步上下文中使用VRL
362        let mut runtime = VrlRuntime::new();
363
364        let script = ".async_test = true";
365        let event = json!({"original": "value"});
366
367        let result = tokio::task::spawn_blocking(move || runtime.run(script, event, "UTC"))
368            .await
369            .unwrap()
370            .unwrap();
371
372        assert_eq!(result.processed_event["original"], "value");
373    }
374}