Skip to main content

openlark_core/error/
context.rs

1//! 现代化错误上下文
2//!
3//! 与 thiserror-based CoreError 完美配合的高性能错误上下文系统
4
5use std::collections::HashMap;
6
7/// 现代化错误上下文
8///
9/// 提供丰富的错误上下文信息,支持结构化数据和链式错误追踪
10#[derive(Debug, Clone)]
11pub struct ErrorContext {
12    /// 用户友好消息
13    user_message: Option<String>,
14    /// 上下文信息
15    context: HashMap<String, String>,
16    /// 时间戳(错误发生时间)
17    timestamp: Option<std::time::SystemTime>,
18    /// 请求ID(用于链路追踪)
19    request_id: Option<String>,
20    /// 操作名称
21    operation: Option<String>,
22    /// 组件名称
23    component: Option<String>,
24}
25
26impl ErrorContext {
27    /// 创建新的错误上下文
28    pub fn new() -> Self {
29        Self {
30            user_message: None,
31            context: HashMap::new(),
32            timestamp: Some(std::time::SystemTime::now()),
33            request_id: None,
34            operation: None,
35            component: None,
36        }
37    }
38
39    /// 创建带有用户消息的错误上下文
40    pub fn with_user_message(message: impl Into<String>) -> Self {
41        Self {
42            user_message: Some(message.into()),
43            context: HashMap::new(),
44            timestamp: Some(std::time::SystemTime::now()),
45            request_id: None,
46            operation: None,
47            component: None,
48        }
49    }
50
51    /// 创建带有操作名的错误上下文
52    pub fn with_operation(operation: impl Into<String>) -> Self {
53        Self {
54            user_message: None,
55            context: HashMap::new(),
56            timestamp: Some(std::time::SystemTime::now()),
57            request_id: None,
58            operation: Some(operation.into()),
59            component: None,
60        }
61    }
62
63    /// 设置用户友好消息
64    pub fn set_user_message(&mut self, message: impl Into<String>) {
65        self.user_message = Some(message.into());
66    }
67
68    /// 获取用户友好消息
69    pub fn user_message(&self) -> Option<&str> {
70        self.user_message.as_deref()
71    }
72
73    /// 添加上下文信息
74    pub fn add_context(&mut self, key: impl Into<String>, value: impl Into<String>) -> &mut Self {
75        self.context.insert(key.into(), value.into());
76        self
77    }
78
79    /// 批量添加上下文信息
80    pub fn extend_context<I, K, V>(&mut self, iter: I) -> &mut Self
81    where
82        I: IntoIterator<Item = (K, V)>,
83        K: Into<String>,
84        V: Into<String>,
85    {
86        self.context
87            .extend(iter.into_iter().map(|(k, v)| (k.into(), v.into())));
88        self
89    }
90
91    /// 获取上下文值
92    pub fn get_context(&self, key: &str) -> Option<&str> {
93        self.context.get(key).map(|s| s.as_str())
94    }
95
96    /// 获取所有上下文信息
97    pub fn all_context(&self) -> &HashMap<String, String> {
98        &self.context
99    }
100
101    /// 检查是否有指定的上下文键
102    pub fn has_context(&self, key: &str) -> bool {
103        self.context.contains_key(key)
104    }
105
106    /// 设置请求ID
107    pub fn set_request_id(&mut self, request_id: impl Into<String>) -> &mut Self {
108        self.request_id = Some(request_id.into());
109        self
110    }
111
112    /// 获取请求ID
113    pub fn request_id(&self) -> Option<&str> {
114        self.request_id.as_deref()
115    }
116
117    /// 设置操作名称
118    pub fn set_operation(&mut self, operation: impl Into<String>) -> &mut Self {
119        self.operation = Some(operation.into());
120        self
121    }
122
123    /// 获取操作名称
124    pub fn operation(&self) -> Option<&str> {
125        self.operation.as_deref()
126    }
127
128    /// 设置组件名称
129    pub fn set_component(&mut self, component: impl Into<String>) -> &mut Self {
130        self.component = Some(component.into());
131        self
132    }
133
134    /// 获取组件名称
135    pub fn component(&self) -> Option<&str> {
136        self.component.as_deref()
137    }
138
139    /// 获取时间戳
140    pub fn timestamp(&self) -> Option<std::time::SystemTime> {
141        self.timestamp
142    }
143
144    /// 清空所有上下文信息
145    /// 清空所有上下文信息
146    pub fn clear_context(&mut self) {
147        self.context.clear();
148    }
149
150    /// 获取上下文信息数量
151    pub fn context_len(&self) -> usize {
152        self.context.len()
153    }
154
155    /// 检查是否为空
156    pub fn is_empty(&self) -> bool {
157        self.user_message.is_none()
158            && self.context.is_empty()
159            && self.request_id.is_none()
160            && self.operation.is_none()
161            && self.component.is_none()
162    }
163
164    /// 转换为调试格式
165    pub fn debug_format(&self) -> String {
166        let mut parts = Vec::new();
167
168        if let Some(timestamp) = self.timestamp {
169            // 手动格式化 SystemTime
170            let duration = timestamp
171                .duration_since(std::time::UNIX_EPOCH)
172                .unwrap_or_default();
173            let secs = duration.as_secs();
174            let datetime =
175                chrono::DateTime::from_timestamp(secs as i64, 0).unwrap_or_else(chrono::Utc::now);
176            parts.push(format!(
177                "时间: {}",
178                datetime.format("%Y-%m-%d %H:%M:%S UTC")
179            ));
180        }
181
182        if let Some(component) = &self.component {
183            parts.push(format!("组件: {}", component));
184        }
185
186        if let Some(operation) = &self.operation {
187            parts.push(format!("操作: {}", operation));
188        }
189
190        if let Some(request_id) = &self.request_id {
191            parts.push(format!("请求ID: {}", request_id));
192        }
193
194        if !self.context.is_empty() {
195            parts.push("上下文:".to_string());
196            for (key, value) in &self.context {
197                parts.push(format!("  {}: {}", key, value));
198            }
199        }
200
201        if let Some(message) = &self.user_message {
202            parts.push(format!("用户消息: {}", message));
203        }
204
205        parts.join("\n")
206    }
207
208    /// 从当前上下文创建副本,并可以添加额外信息
209    pub fn clone_with(&self) -> Self {
210        let mut clone = self.clone();
211        clone.timestamp = Some(std::time::SystemTime::now());
212        clone
213    }
214}
215
216impl Default for ErrorContext {
217    fn default() -> Self {
218        Self::new()
219    }
220}
221
222/// 错误上下文构建器
223///
224/// 提供流式API来构建复杂的错误上下文
225#[derive(Debug, Clone)]
226pub struct ErrorContextBuilder {
227    context: ErrorContext,
228}
229
230impl ErrorContextBuilder {
231    /// 创建新的构建器
232    pub fn new() -> Self {
233        Self {
234            context: ErrorContext::new(),
235        }
236    }
237
238    /// 设置用户消息
239    pub fn user_message(mut self, message: impl Into<String>) -> Self {
240        self.context.set_user_message(message);
241        self
242    }
243
244    /// 添加上下文信息
245    pub fn context(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
246        self.context.add_context(key, value);
247        self
248    }
249
250    /// 批量添加上下文信息
251    pub fn extend_context<I, K, V>(mut self, iter: I) -> Self
252    where
253        I: IntoIterator<Item = (K, V)>,
254        K: Into<String>,
255        V: Into<String>,
256    {
257        self.context.extend_context(iter);
258        self
259    }
260
261    /// 设置请求ID
262    pub fn request_id(mut self, request_id: impl Into<String>) -> Self {
263        self.context.set_request_id(request_id);
264        self
265    }
266
267    /// 设置操作名称
268    pub fn operation(mut self, operation: impl Into<String>) -> Self {
269        self.context.set_operation(operation);
270        self
271    }
272
273    /// 设置组件名称
274    pub fn component(mut self, component: impl Into<String>) -> Self {
275        self.context.set_component(component);
276        self
277    }
278
279    /// 构建错误上下文
280    pub fn build(self) -> ErrorContext {
281        self.context
282    }
283}
284
285impl Default for ErrorContextBuilder {
286    fn default() -> Self {
287        Self::new()
288    }
289}
290
291impl std::fmt::Display for ErrorContext {
292    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
293        write!(f, "{}", self.debug_format())
294    }
295}
296
297#[cfg(test)]
298mod tests {
299    use super::*;
300
301    #[test]
302    fn test_error_context_basic() {
303        let mut context = ErrorContext::new();
304        context.add_context("endpoint", "https://api.example.com");
305        context.set_user_message("连接失败");
306
307        assert!(context.has_context("endpoint"));
308        assert_eq!(
309            context.get_context("endpoint"),
310            Some("https://api.example.com")
311        );
312        assert_eq!(context.user_message(), Some("连接失败"));
313    }
314
315    #[test]
316    fn test_error_context_constructors() {
317        let context_with_msg = ErrorContext::with_user_message("测试消息");
318        assert_eq!(context_with_msg.user_message(), Some("测试消息"));
319
320        let context_with_op = ErrorContext::with_operation("api_call");
321        assert_eq!(context_with_op.operation(), Some("api_call"));
322    }
323
324    #[test]
325    fn test_error_context_builder() {
326        let context = ErrorContextBuilder::new()
327            .user_message("构建器测试")
328            .context("url", "https://example.com")
329            .context("method", "GET")
330            .operation("http_request")
331            .component("http_client")
332            .request_id("req-123")
333            .build();
334
335        assert_eq!(context.user_message(), Some("构建器测试"));
336        assert_eq!(context.get_context("url"), Some("https://example.com"));
337        assert_eq!(context.get_context("method"), Some("GET"));
338        assert_eq!(context.operation(), Some("http_request"));
339        assert_eq!(context.component(), Some("http_client"));
340        assert_eq!(context.request_id(), Some("req-123"));
341    }
342
343    #[test]
344    fn test_error_context_chain_operations() {
345        let mut context = ErrorContext::new()
346            .add_context("key1", "value1")
347            .add_context("key2", "value2")
348            .clone_with();
349
350        context
351            .add_context("key3", "value3")
352            .set_operation("test_operation");
353
354        assert_eq!(context.get_context("key1"), Some("value1"));
355        assert_eq!(context.get_context("key2"), Some("value2"));
356        assert_eq!(context.get_context("key3"), Some("value3"));
357        assert_eq!(context.operation(), Some("test_operation"));
358    }
359
360    #[test]
361    fn test_error_context_extend() {
362        let mut context = ErrorContext::new();
363        context.extend_context(vec![
364            ("key1", "value1"),
365            ("key2", "value2"),
366            ("key3", "value3"),
367        ]);
368
369        assert_eq!(context.context_len(), 3);
370        assert!(context.has_context("key1"));
371        assert!(context.has_context("key2"));
372        assert!(context.has_context("key3"));
373    }
374
375    #[test]
376    fn test_error_context_debug_format() {
377        let context = ErrorContextBuilder::new()
378            .user_message("测试错误")
379            .context("api", "send_message")
380            .operation("message_send")
381            .component("communication")
382            .request_id("req-456")
383            .build();
384
385        let debug_str = context.debug_format();
386        assert!(debug_str.contains("测试错误"));
387        assert!(debug_str.contains("api"));
388        assert!(debug_str.contains("send_message"));
389        assert!(debug_str.contains("message_send"));
390        assert!(debug_str.contains("communication"));
391        assert!(debug_str.contains("req-456"));
392        assert!(debug_str.contains("时间:"));
393    }
394
395    #[test]
396    fn test_error_context_is_empty() {
397        let empty_context = ErrorContext::new();
398        assert!(empty_context.is_empty());
399
400        let mut non_empty_context = ErrorContext::new();
401        non_empty_context.add_context("test", "value");
402        assert!(!non_empty_context.is_empty());
403    }
404
405    #[test]
406    fn test_error_context_clone_with() {
407        let mut original = ErrorContext::new();
408        original.add_context("original", "data");
409        original.set_user_message("原始消息");
410
411        let cloned = original.clone_with();
412
413        // 验证所有数据都被复制
414        assert_eq!(cloned.get_context("original"), Some("data"));
415        assert_eq!(cloned.user_message(), Some("原始消息"));
416
417        // 验证时间戳被更新
418        assert!(cloned.timestamp() > original.timestamp());
419    }
420}