1use openlark_core::error::{CoreError, ErrorTrait, ErrorType};
7
8pub type Error = CoreError;
12
13pub type Result<T> = std::result::Result<T, Error>;
15
16pub fn network_error(message: impl Into<String>) -> Error {
22 openlark_core::error::network_error(message)
23}
24
25pub fn authentication_error(message: impl Into<String>) -> Error {
27 openlark_core::error::authentication_error(message)
28}
29
30pub fn token_invalid_error(detail: impl Into<String>) -> Error {
32 openlark_core::error::token_invalid_error(detail)
33}
34
35pub fn token_expired_error(detail: impl Into<String>) -> Error {
37 openlark_core::error::token_expired_error(detail)
38}
39
40pub fn permission_missing_error(scopes: &[impl AsRef<str>]) -> Error {
42 openlark_core::error::permission_missing_error(scopes)
43}
44
45pub fn sso_token_invalid_error(detail: impl Into<String>) -> Error {
47 openlark_core::error::sso_token_invalid_error(detail)
48}
49
50pub fn user_identity_invalid_error(desc: impl Into<String>) -> Error {
52 openlark_core::error::user_identity_invalid_error(desc)
53}
54
55pub fn api_error(
57 raw_code: i32,
58 endpoint: impl Into<String>,
59 message: impl Into<String>,
60 request_id: Option<String>,
61) -> Error {
62 openlark_core::error::api_error(raw_code, endpoint, message, request_id)
63}
64
65pub fn validation_error(field: impl Into<String>, message: impl Into<String>) -> Error {
67 openlark_core::error::validation_error(field, message)
68}
69
70pub fn configuration_error(message: impl Into<String>) -> Error {
72 openlark_core::error::configuration_error(message)
73}
74
75pub fn serialization_error(message: impl Into<String>) -> Error {
77 openlark_core::error::serialization_error(message, None::<serde_json::Error>)
78}
79
80pub fn business_error(_code: impl Into<String>, message: impl Into<String>) -> Error {
82 openlark_core::error::business_error(message)
83}
84
85pub fn timeout_error(operation: impl Into<String>) -> Error {
87 use std::time::Duration;
88 openlark_core::error::timeout_error(Duration::from_secs(30), Some(operation.into()))
89}
90
91pub fn rate_limit_error(retry_after: Option<u64>) -> Error {
93 use std::time::Duration;
94 openlark_core::error::rate_limit_error(
95 100,
96 Duration::from_secs(60),
97 retry_after.map(Duration::from_secs),
98 )
99}
100
101pub fn service_unavailable_error(service: impl Into<String>) -> Error {
103 use std::time::Duration;
104 openlark_core::error::service_unavailable_error(service, Some(Duration::from_secs(60)))
105}
106
107pub fn internal_error(message: impl Into<String>) -> Error {
109 openlark_core::error::api_error(500, "internal", message, None::<String>)
110}
111
112pub trait ClientErrorExt {
118 fn suggestion(&self) -> &'static str;
120
121 fn recovery_steps(&self) -> Vec<&'static str>;
123}
124
125impl ClientErrorExt for Error {
126 fn suggestion(&self) -> &'static str {
127 match self.error_type() {
128 ErrorType::Network => "检查网络连接,确认防火墙设置",
129 ErrorType::Authentication => "验证应用凭据,检查令牌有效性",
130 ErrorType::Api => "检查API参数,确认请求格式正确",
131 ErrorType::Validation => "验证输入参数格式和范围",
132 ErrorType::Configuration => "检查应用配置文件和环境变量",
133 ErrorType::Serialization => "确认数据格式正确,检查JSON结构",
134 ErrorType::Business => "确认业务逻辑条件,检查相关权限",
135 ErrorType::Timeout => "增加超时时间或优化请求内容",
136 ErrorType::RateLimit => "稍后重试,考虑降低请求频率",
137 ErrorType::ServiceUnavailable => "稍后重试,检查服务状态",
138 ErrorType::Internal => "联系技术支持,提供错误详情",
139 ErrorType::ResponseTooLarge => "减小请求数据量或增大 max_response_size 配置",
140 }
141 }
142
143 fn recovery_steps(&self) -> Vec<&'static str> {
144 match self.error_type() {
145 ErrorType::Network => vec![
146 "检查网络连接状态",
147 "确认代理设置正确",
148 "验证防火墙规则",
149 "尝试切换网络环境",
150 ],
151 ErrorType::Authentication => vec![
152 "验证应用ID和密钥正确性",
153 "检查令牌是否过期",
154 "确认应用权限配置",
155 "重新生成访问令牌",
156 ],
157 ErrorType::Api => vec![
158 "检查请求参数格式",
159 "确认API端点正确",
160 "验证请求体结构",
161 "查阅API文档",
162 ],
163 ErrorType::Validation => vec![
164 "检查必填字段",
165 "验证数据格式和范围",
166 "确认字段类型正确",
167 "参考输入示例",
168 ],
169 ErrorType::Configuration => vec![
170 "检查环境变量设置",
171 "验证配置文件格式",
172 "确认应用权限配置",
173 "重新加载配置",
174 ],
175 ErrorType::Serialization => vec![
176 "检查JSON格式正确性",
177 "验证数据结构匹配",
178 "确认字段类型一致",
179 "使用在线JSON验证工具",
180 ],
181 ErrorType::Business => vec![
182 "检查业务规则约束",
183 "确认用户权限充分",
184 "验证数据完整性",
185 "联系业务负责人",
186 ],
187 ErrorType::Timeout => vec![
188 "增加请求超时时间",
189 "优化网络环境",
190 "减少请求数据量",
191 "考虑分批处理",
192 ],
193 ErrorType::RateLimit => vec![
194 "等待后重试",
195 "降低请求频率",
196 "实施退避策略",
197 "联系技术支持提高限额",
198 ],
199 ErrorType::ServiceUnavailable => vec![
200 "稍后重试请求",
201 "检查服务状态页面",
202 "切换到备用方案",
203 "联系技术支持",
204 ],
205 ErrorType::Internal => vec![
206 "记录详细错误信息",
207 "检查系统日志",
208 "重启相关服务",
209 "联系技术支持",
210 ],
211 ErrorType::ResponseTooLarge => vec![
212 "减小请求数据量",
213 "增大 max_response_size 配置",
214 "分批请求数据",
215 "联系技术支持确认数据规模",
216 ],
217 }
218 }
219}
220
221pub fn from_sdk_result<T>(result: openlark_core::SDKResult<T>) -> Result<T> {
249 result
250}
251
252pub fn with_context<T>(
254 result: Result<T>,
255 context_key: impl Into<String>,
256 context_value: impl Into<String>,
257) -> Result<T> {
258 let key = context_key.into();
259 let value = context_value.into();
260 result.map_err(|err| err.with_context_kv(key, value))
261}
262
263pub fn with_operation_context<T>(
265 result: Result<T>,
266 operation: impl Into<String>,
267 component: impl Into<String>,
268) -> Result<T> {
269 let operation = operation.into();
270 let component = component.into();
271 result.map_err(|err| err.with_operation(operation, component))
272}
273
274#[derive(Debug)]
280pub struct ErrorAnalyzer<'a> {
281 error: &'a Error,
282}
283
284impl<'a> ErrorAnalyzer<'a> {
285 pub fn new(error: &'a Error) -> Self {
287 Self { error }
288 }
289
290 pub fn detailed_report(&self) -> String {
292 let mut report = String::new();
293
294 report.push_str("🚨 错误分析报告\n");
295 report.push_str("================\n\n");
296
297 report.push_str("📋 基本信息:\n");
299 report.push_str(&format!(" 错误类型: {:?}\n", self.error.error_type()));
300 report.push_str(&format!(" 错误代码: {:?}\n", self.error.error_code()));
301 report.push_str(&format!(" 严重程度: {:?}\n", self.error.severity()));
302 report.push_str(&format!(" 可重试: {}\n", self.error.is_retryable()));
303
304 if let Some(request_id) = self.error.context().request_id() {
305 report.push_str(&format!(" 请求ID: {request_id}\n"));
306 }
307
308 report.push('\n');
309
310 report.push_str("💬 错误消息:\n");
312 report.push_str(&format!(" 技术消息: {}\n", self.error));
313 report.push_str(&format!(
314 " 用户消息: {}\n",
315 self.error.user_message().unwrap_or("未知错误")
316 ));
317
318 report.push('\n');
319
320 report.push_str("💡 建议:\n");
322 report.push_str(&format!(" {}\n", self.error.suggestion()));
323
324 report.push_str("\n🔧 恢复步骤:\n");
325 for (i, step) in self.error.recovery_steps().iter().enumerate() {
326 report.push_str(&format!(" {}. {}\n", i + 1, step));
327 }
328
329 report.push('\n');
330
331 if self.error.context().context_len() > 0 {
333 report.push_str("📊 上下文信息:\n");
334 for (key, value) in self.error.context().all_context() {
335 report.push_str(&format!(" {key}: {value}\n"));
336 }
337 report.push('\n');
338 }
339
340 if let Some(timestamp) = self.error.context().timestamp() {
342 report.push_str(&format!("⏰ 发生时间: {timestamp:?}\n"));
343 }
344 report
345 }
346
347 pub fn log_summary(&self) -> String {
349 format!(
350 "Error[{:?}:{:?}] {} - {}",
351 self.error.error_type(),
352 self.error.error_code(),
353 self.error.user_message().unwrap_or("未知错误"),
354 if self.error.is_retryable() {
355 "(可重试)"
356 } else {
357 "(不可重试)"
358 }
359 )
360 }
361
362 pub fn user_friendly_with_suggestion(&self) -> String {
364 format!(
365 "{}\n\n💡 建议: {}\n\n🔧 可以尝试:\n{}",
366 self.error.user_message().unwrap_or("未知错误"),
367 self.error.suggestion(),
368 self.error
369 .recovery_steps()
370 .iter()
371 .enumerate()
372 .map(|(i, step)| format!("{}. {}", i + 1, step))
373 .collect::<Vec<_>>()
374 .join("\n")
375 )
376 }
377}
378
379#[cfg(test)]
387#[allow(unused_imports)]
388mod tests {
389 use super::*;
390
391 #[test]
392 fn test_error_convenience_functions() {
393 let network_err = network_error("连接失败");
394 assert!(network_err.is_network_error());
395 assert!(network_err.is_retryable());
396
397 let auth_err = authentication_error("令牌无效");
398 assert!(auth_err.is_auth_error());
399 assert!(!auth_err.is_retryable());
400
401 let validation_err = validation_error("email", "邮箱格式不正确");
402 assert!(validation_err.is_validation_error());
403 assert!(!validation_err.is_retryable());
404 }
405
406 #[test]
407 fn test_error_analyzer() {
408 let error = api_error(404, "/users", "用户不存在", Some("req-123".to_string()));
409 let analyzer = ErrorAnalyzer::new(&error);
410
411 let report = analyzer.detailed_report();
412 assert!(report.contains("错误分析报告"));
413 assert!(report.contains("API错误"));
414 assert!(report.contains("req-123"));
415
416 let summary = analyzer.log_summary();
417 assert!(summary.contains("Error"));
418 assert!(summary.contains("Api"));
419
420 let user_msg = analyzer.user_friendly_with_suggestion();
421 assert!(user_msg.contains("建议"));
422 assert!(user_msg.contains("可以尝试"));
423 }
424
425 #[test]
426 fn test_client_error_ext() {
427 let error = timeout_error("数据同步");
428
429 assert!(!error.is_network_error());
430 assert!(!error.is_auth_error());
431 assert!(!error.is_business_error());
432 assert!(error.is_retryable());
433
434 let suggestion = error.suggestion();
435 assert!(!suggestion.is_empty());
436
437 let steps = error.recovery_steps();
438 assert!(!steps.is_empty());
439 assert!(steps.contains(&"增加请求超时时间"));
440 }
441
442 #[test]
443 fn test_error_conversions() {
444 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
446 let error: Error = json_err.into();
447 assert!(error.is_serialization_error());
448
449 }
455
456 #[test]
457 fn test_context_functions() {
458 let result: Result<i32> = Err(validation_error("age", "年龄不能为负数"));
459
460 let contextual_result = with_context(result, "user_id", "12345");
461 assert!(contextual_result.is_err());
462
463 let error = contextual_result.unwrap_err();
464 assert_eq!(error.context().get_context("user_id"), Some("12345"));
467 }
468
469 #[test]
470 fn test_sdk_result_conversion() {
471 let core_result: openlark_core::SDKResult<String> = Ok("success".to_string());
473 let client_result: Result<String> = from_sdk_result(core_result);
474 assert!(client_result.is_ok());
475 assert_eq!(client_result.unwrap(), "success");
476
477 let core_result: openlark_core::SDKResult<String> = Err(network_error("网络错误"));
479 let client_result: Result<String> = from_sdk_result(core_result);
480 assert!(client_result.is_err());
481 assert!(client_result.unwrap_err().is_network_error());
482 }
483
484 #[test]
485 fn test_api_error_function() {
486 let error = api_error(
487 500,
488 "/api/test",
489 "服务器内部错误",
490 Some("req-456".to_string()),
491 );
492 assert!(error.is_api_error());
493 let analyzer = ErrorAnalyzer::new(&error);
494 let report = analyzer.detailed_report();
495 assert!(report.contains("服务器内部错误"));
496 }
497
498 #[test]
499 fn test_validation_error_function() {
500 let error = validation_error("field_name", "字段值为空");
501 assert!(error.is_validation_error());
502 let analyzer = ErrorAnalyzer::new(&error);
503 let user_msg = analyzer.user_friendly_with_suggestion();
504 assert!(user_msg.contains("建议"));
505 }
506
507 #[test]
508 fn test_configuration_error_function() {
509 let error = configuration_error("配置文件缺失");
510 assert!(error.is_config_error());
511 }
512
513 #[test]
514 fn test_serialization_error_function() {
515 let error = serialization_error("JSON解析失败");
516 assert!(error.is_serialization_error());
517 }
518
519 #[test]
520 fn test_business_error_function() {
521 let error = business_error("ERR_001", "业务规则验证失败");
522 assert!(error.is_business_error());
523 }
524
525 #[test]
526 fn test_timeout_error_function() {
527 let error = timeout_error("数据库查询超时");
528 assert!(error.is_timeout_error());
529 assert!(error.is_retryable());
530 }
531
532 #[test]
533 fn test_rate_limit_error_function() {
534 let error = rate_limit_error(Some(60));
535 assert!(error.is_rate_limited());
536 }
537
538 #[test]
539 fn test_service_unavailable_error_function() {
540 let error = service_unavailable_error("支付服务");
541 assert!(error.is_service_unavailable_error());
542 }
543
544 #[test]
545 fn test_internal_error_function() {
546 let error = internal_error("系统内部错误");
547 assert!(!error.is_user_error());
548 }
549
550 #[test]
551 fn test_token_invalid_error_function() {
552 let error = token_invalid_error("token格式不正确");
553 assert!(error.is_auth_error());
554 }
555
556 #[test]
557 fn test_token_expired_error_function() {
558 let error = token_expired_error("token已过期");
559 assert!(error.is_auth_error());
560 }
561
562 #[test]
563 fn test_permission_missing_error_function() {
564 let scopes = vec!["read:user", "write:docs"];
565 let error = permission_missing_error(&scopes);
566 assert!(error.is_auth_error());
567 }
568
569 #[test]
570 fn test_sso_token_invalid_error_function() {
571 let error = sso_token_invalid_error("SSO令牌无效");
572 assert!(error.is_auth_error());
573 }
574
575 #[test]
576 fn test_user_identity_invalid_error_function() {
577 let error = user_identity_invalid_error("用户身份标识非法");
578 assert!(error.is_auth_error());
579 }
580
581 #[test]
582 fn test_error_analyzer_log_summary() {
583 let error = network_error("连接超时");
584 let analyzer = ErrorAnalyzer::new(&error);
585 let summary = analyzer.log_summary();
586 assert!(summary.contains("Network"));
587 assert!(summary.contains("可重试") || summary.contains("不可重试"));
588 }
589
590 #[test]
591 fn test_error_analyzer_user_friendly() {
592 let error = api_error(404, "/users/123", "用户不存在", None);
593 let analyzer = ErrorAnalyzer::new(&error);
594 let friendly = analyzer.user_friendly_with_suggestion();
595 assert!(friendly.contains("建议"));
596 assert!(friendly.contains("可以尝试"));
597 }
598
599 #[test]
600 fn test_with_operation_context() {
601 let result: Result<i32> = Err(network_error("网络错误"));
602 let contextual_result = with_operation_context(result, "test_operation", "TestComponent");
603 assert!(contextual_result.is_err());
604 let error = contextual_result.unwrap_err();
605 assert_eq!(
606 error.context().get_context("operation"),
607 Some("test_operation")
608 );
609 assert_eq!(
610 error.context().get_context("component"),
611 Some("TestComponent")
612 );
613 }
614
615 #[test]
616 fn test_with_operation_context_updates_timeout_operation_field() {
617 use std::time::Duration;
618
619 let result: Result<i32> = Err(openlark_core::error::timeout_error(
620 Duration::from_secs(3),
621 Some("old_operation".to_string()),
622 ));
623
624 let contextual_result = with_operation_context(result, "new_operation", "ClientLayer");
625 assert!(contextual_result.is_err());
626
627 match contextual_result.unwrap_err() {
628 CoreError::Timeout {
629 operation, ref ctx, ..
630 } => {
631 assert_eq!(operation.as_deref(), Some("new_operation"));
632 assert_eq!(ctx.operation(), Some("new_operation"));
633 assert_eq!(ctx.component(), Some("ClientLayer"));
634 }
635 other => panic!("expected timeout error, got {:?}", other.error_type()),
636 }
637 }
638
639 #[test]
640 fn test_all_error_types_suggestion() {
641 let error_types = vec![
642 (network_error("test"), "检查网络连接"),
643 (authentication_error("test"), "验证应用凭据"),
644 (api_error(500, "/test", "test", None), "检查API参数"),
645 (validation_error("field", "test"), "验证输入参数"),
646 (configuration_error("test"), "检查应用配置"),
647 (serialization_error("test"), "确认数据格式"),
648 (business_error("code", "test"), "确认业务逻辑"),
649 (timeout_error("test"), "增加超时时间"),
650 (rate_limit_error(None), "稍后重试"),
651 (service_unavailable_error("svc"), "稍后重试"),
652 (internal_error("test"), "联系技术支持"),
653 ];
654
655 for (error, expected_keyword) in error_types {
656 let suggestion = error.suggestion();
657 assert!(
658 suggestion.contains(expected_keyword) || !suggestion.is_empty(),
659 "Error type {:?} should have meaningful suggestion",
660 error.error_type()
661 );
662 }
663 }
664
665 #[test]
666 fn test_all_error_types_recovery_steps() {
667 let error_types = vec![
668 network_error("test"),
669 authentication_error("test"),
670 api_error(500, "/test", "test", None),
671 validation_error("field", "test"),
672 configuration_error("test"),
673 serialization_error("test"),
674 business_error("code", "test"),
675 timeout_error("test"),
676 rate_limit_error(None),
677 service_unavailable_error("svc"),
678 internal_error("test"),
679 ];
680
681 for error in error_types {
682 let steps = error.recovery_steps();
683 assert!(
684 !steps.is_empty(),
685 "Error type {:?} should have recovery steps",
686 error.error_type()
687 );
688 assert!(
689 steps.len() >= 3,
690 "Error type {:?} should have at least 3 recovery steps",
691 error.error_type()
692 );
693 }
694 }
695}