1use crate::registry::RegistryError;
7use openlark_core::error::{
8 ApiError, CoreError, ErrorCategory, ErrorCode, ErrorContext, ErrorTrait, ErrorType,
9};
10
11pub type Error = CoreError;
15
16pub type Result<T> = std::result::Result<T, Error>;
18
19pub fn network_error(message: impl Into<String>) -> Error {
25 openlark_core::error::network_error(message)
26}
27
28pub fn authentication_error(message: impl Into<String>) -> Error {
30 openlark_core::error::authentication_error(message)
31}
32
33pub fn token_invalid_error(detail: impl Into<String>) -> Error {
35 openlark_core::error::token_invalid_error(detail)
36}
37
38pub fn token_expired_error(detail: impl Into<String>) -> Error {
40 openlark_core::error::token_expired_error(detail)
41}
42
43pub fn permission_missing_error(scopes: &[impl AsRef<str>]) -> Error {
45 openlark_core::error::permission_missing_error(scopes)
46}
47
48pub fn sso_token_invalid_error(detail: impl Into<String>) -> Error {
50 openlark_core::error::sso_token_invalid_error(detail)
51}
52
53pub fn user_identity_invalid_error(desc: impl Into<String>) -> Error {
55 openlark_core::error::user_identity_invalid_error(desc)
56}
57
58pub fn from_feishu_response(
60 code: i32,
61 endpoint: impl Into<String>,
62 message: impl Into<String>,
63 request_id: Option<String>,
64) -> Error {
65 let mapped = ErrorCode::from_feishu_code(code).unwrap_or_else(|| ErrorCode::from_code(code));
66
67 let mut ctx = ErrorContext::new();
68 ctx.add_context("feishu_code", code.to_string());
69 if let Some(req) = request_id {
70 ctx.set_request_id(req);
71 }
72
73 let status = mapped
74 .http_status()
75 .unwrap_or_else(|| match mapped.category() {
76 ErrorCategory::RateLimit => 429,
77 ErrorCategory::Authentication
78 | ErrorCategory::Permission
79 | ErrorCategory::Parameter => 400,
80 ErrorCategory::Resource => 404,
81 _ => 500,
82 });
83
84 CoreError::Api(Box::new(ApiError {
85 status,
86 endpoint: endpoint.into().into(),
87 message: message.into(),
88 source: None,
89 code: mapped,
90 ctx: Box::new(ctx),
91 }))
92}
93
94pub fn api_error(
96 status: u16,
97 endpoint: impl Into<String>,
98 message: impl Into<String>,
99 request_id: Option<String>,
100) -> Error {
101 openlark_core::error::api_error(status, endpoint, message, request_id)
102}
103
104pub fn validation_error(field: impl Into<String>, message: impl Into<String>) -> Error {
106 openlark_core::error::validation_error(field, message)
107}
108
109pub fn configuration_error(message: impl Into<String>) -> Error {
111 openlark_core::error::configuration_error(message)
112}
113
114pub fn serialization_error(message: impl Into<String>) -> Error {
116 openlark_core::error::serialization_error(message, None::<serde_json::Error>)
117}
118
119pub fn business_error(_code: impl Into<String>, message: impl Into<String>) -> Error {
121 openlark_core::error::business_error(message)
122}
123
124pub fn timeout_error(operation: impl Into<String>) -> Error {
126 use std::time::Duration;
127 openlark_core::error::timeout_error(Duration::from_secs(30), Some(operation.into()))
128}
129
130pub fn rate_limit_error(retry_after: Option<u64>) -> Error {
132 use std::time::Duration;
133 openlark_core::error::rate_limit_error(
134 100,
135 Duration::from_secs(60),
136 retry_after.map(Duration::from_secs),
137 )
138}
139
140pub fn service_unavailable_error(service: impl Into<String>) -> Error {
142 use std::time::Duration;
143 openlark_core::error::service_unavailable_error(service, Some(Duration::from_secs(60)))
144}
145
146pub fn internal_error(message: impl Into<String>) -> Error {
148 openlark_core::error::api_error(500, "internal", message, None::<String>)
149}
150
151pub fn registry_error(err: RegistryError) -> Error {
153 internal_error(format!("服务注册表错误: {}", err))
154}
155
156pub trait ClientErrorExt {
162 fn suggestion(&self) -> &'static str;
164
165 fn recovery_steps(&self) -> Vec<&'static str>;
167}
168
169impl ClientErrorExt for Error {
170 fn suggestion(&self) -> &'static str {
171 match self.error_type() {
172 ErrorType::Network => "检查网络连接,确认防火墙设置",
173 ErrorType::Authentication => "验证应用凭据,检查令牌有效性",
174 ErrorType::Api => "检查API参数,确认请求格式正确",
175 ErrorType::Validation => "验证输入参数格式和范围",
176 ErrorType::Configuration => "检查应用配置文件和环境变量",
177 ErrorType::Serialization => "确认数据格式正确,检查JSON结构",
178 ErrorType::Business => "确认业务逻辑条件,检查相关权限",
179 ErrorType::Timeout => "增加超时时间或优化请求内容",
180 ErrorType::RateLimit => "稍后重试,考虑降低请求频率",
181 ErrorType::ServiceUnavailable => "稍后重试,检查服务状态",
182 ErrorType::Internal => "联系技术支持,提供错误详情",
183 }
184 }
185
186 fn recovery_steps(&self) -> Vec<&'static str> {
187 match self.error_type() {
188 ErrorType::Network => vec![
189 "检查网络连接状态",
190 "确认代理设置正确",
191 "验证防火墙规则",
192 "尝试切换网络环境",
193 ],
194 ErrorType::Authentication => vec![
195 "验证应用ID和密钥正确性",
196 "检查令牌是否过期",
197 "确认应用权限配置",
198 "重新生成访问令牌",
199 ],
200 ErrorType::Api => vec![
201 "检查请求参数格式",
202 "确认API端点正确",
203 "验证请求体结构",
204 "查阅API文档",
205 ],
206 ErrorType::Validation => vec![
207 "检查必填字段",
208 "验证数据格式和范围",
209 "确认字段类型正确",
210 "参考输入示例",
211 ],
212 ErrorType::Configuration => vec![
213 "检查环境变量设置",
214 "验证配置文件格式",
215 "确认应用权限配置",
216 "重新加载配置",
217 ],
218 ErrorType::Serialization => vec![
219 "检查JSON格式正确性",
220 "验证数据结构匹配",
221 "确认字段类型一致",
222 "使用在线JSON验证工具",
223 ],
224 ErrorType::Business => vec![
225 "检查业务规则约束",
226 "确认用户权限充分",
227 "验证数据完整性",
228 "联系业务负责人",
229 ],
230 ErrorType::Timeout => vec![
231 "增加请求超时时间",
232 "优化网络环境",
233 "减少请求数据量",
234 "考虑分批处理",
235 ],
236 ErrorType::RateLimit => vec![
237 "等待后重试",
238 "降低请求频率",
239 "实施退避策略",
240 "联系技术支持提高限额",
241 ],
242 ErrorType::ServiceUnavailable => vec![
243 "稍后重试请求",
244 "检查服务状态页面",
245 "切换到备用方案",
246 "联系技术支持",
247 ],
248 ErrorType::Internal => vec![
249 "记录详细错误信息",
250 "检查系统日志",
251 "重启相关服务",
252 "联系技术支持",
253 ],
254 }
255 }
256}
257
258impl From<RegistryError> for Error {
270 fn from(err: RegistryError) -> Self {
271 registry_error(err)
272 }
273}
274
275pub fn from_sdk_result<T>(result: openlark_core::SDKResult<T>) -> Result<T> {
293 result
294}
295
296pub fn with_context<T>(
298 result: Result<T>,
299 context_key: impl Into<String>,
300 context_value: impl Into<String>,
301) -> Result<T> {
302 let key = context_key.into();
303 let value = context_value.into();
304 result.map_err(|err| err.with_context_kv(key, value))
305}
306
307pub fn with_operation_context<T>(
309 result: Result<T>,
310 operation: impl Into<String>,
311 component: impl Into<String>,
312) -> Result<T> {
313 let operation = operation.into();
314 let component = component.into();
315 result.map_err(|err| err.with_operation(operation, component))
316}
317
318#[derive(Debug)]
324pub struct ErrorAnalyzer<'a> {
325 error: &'a Error,
326}
327
328impl<'a> ErrorAnalyzer<'a> {
329 pub fn new(error: &'a Error) -> Self {
331 Self { error }
332 }
333
334 pub fn detailed_report(&self) -> String {
336 let mut report = String::new();
337
338 report.push_str("🚨 错误分析报告\n");
339 report.push_str("================\n\n");
340
341 report.push_str("📋 基本信息:\n");
343 report.push_str(&format!(" 错误类型: {:?}\n", self.error.error_type()));
344 report.push_str(&format!(" 错误代码: {:?}\n", self.error.error_code()));
345 report.push_str(&format!(" 严重程度: {:?}\n", self.error.severity()));
346 report.push_str(&format!(" 可重试: {}\n", self.error.is_retryable()));
347
348 if let Some(request_id) = self.error.context().request_id() {
349 report.push_str(&format!(" 请求ID: {}\n", request_id));
350 }
351
352 report.push('\n');
353
354 report.push_str("💬 错误消息:\n");
356 report.push_str(&format!(" 技术消息: {}\n", self.error));
357 report.push_str(&format!(
358 " 用户消息: {}\n",
359 self.error.user_message().unwrap_or("未知错误")
360 ));
361
362 report.push('\n');
363
364 report.push_str("💡 建议:\n");
366 report.push_str(&format!(" {}\n", self.error.suggestion()));
367
368 report.push_str("\n🔧 恢复步骤:\n");
369 for (i, step) in self.error.recovery_steps().iter().enumerate() {
370 report.push_str(&format!(" {}. {}\n", i + 1, step));
371 }
372
373 report.push('\n');
374
375 if self.error.context().context_len() > 0 {
377 report.push_str("📊 上下文信息:\n");
378 for (key, value) in self.error.context().all_context() {
379 report.push_str(&format!(" {}: {}\n", key, value));
380 }
381 report.push('\n');
382 }
383
384 if let Some(timestamp) = self.error.context().timestamp() {
386 report.push_str(&format!("⏰ 发生时间: {:?}\n", timestamp));
387 }
388 report
389 }
390
391 pub fn log_summary(&self) -> String {
393 format!(
394 "Error[{:?}:{:?}] {} - {}",
395 self.error.error_type(),
396 self.error.error_code(),
397 self.error.user_message().unwrap_or("未知错误"),
398 if self.error.is_retryable() {
399 "(可重试)"
400 } else {
401 "(不可重试)"
402 }
403 )
404 }
405
406 pub fn user_friendly_with_suggestion(&self) -> String {
408 format!(
409 "{}\n\n💡 建议: {}\n\n🔧 可以尝试:\n{}",
410 self.error.user_message().unwrap_or("未知错误"),
411 self.error.suggestion(),
412 self.error
413 .recovery_steps()
414 .iter()
415 .enumerate()
416 .map(|(i, step)| format!("{}. {}", i + 1, step))
417 .collect::<Vec<_>>()
418 .join("\n")
419 )
420 }
421}
422
423#[cfg(test)]
431#[allow(unused_imports)]
432mod tests {
433 use super::*;
434
435 #[test]
436 fn test_error_convenience_functions() {
437 let network_err = network_error("连接失败");
438 assert!(network_err.is_network_error());
439 assert!(network_err.is_retryable());
440
441 let auth_err = authentication_error("令牌无效");
442 assert!(auth_err.is_auth_error());
443 assert!(!auth_err.is_retryable());
444
445 let validation_err = validation_error("email", "邮箱格式不正确");
446 assert!(validation_err.is_validation_error());
447 assert!(!validation_err.is_retryable());
448 }
449
450 #[test]
451 fn test_error_analyzer() {
452 let error = api_error(404, "/users", "用户不存在", Some("req-123".to_string()));
453 let analyzer = ErrorAnalyzer::new(&error);
454
455 let report = analyzer.detailed_report();
456 assert!(report.contains("错误分析报告"));
457 assert!(report.contains("API错误"));
458 assert!(report.contains("req-123"));
459
460 let summary = analyzer.log_summary();
461 assert!(summary.contains("Error"));
462 assert!(summary.contains("Api"));
463
464 let user_msg = analyzer.user_friendly_with_suggestion();
465 assert!(user_msg.contains("建议"));
466 assert!(user_msg.contains("可以尝试"));
467 }
468
469 #[test]
470 fn test_client_error_ext() {
471 let error = timeout_error("数据同步");
472
473 assert!(!error.is_network_error());
474 assert!(!error.is_auth_error());
475 assert!(!error.is_business_error());
476 assert!(error.is_retryable());
477
478 let suggestion = error.suggestion();
479 assert!(!suggestion.is_empty());
480
481 let steps = error.recovery_steps();
482 assert!(!steps.is_empty());
483 assert!(steps.contains(&"增加请求超时时间"));
484 }
485
486 #[test]
487 fn test_error_conversions() {
488 let json_err = serde_json::from_str::<serde_json::Value>("invalid json").unwrap_err();
490 let error: Error = json_err.into();
491 assert!(error.is_serialization_error());
492
493 }
499
500 #[test]
501 fn test_context_functions() {
502 let result: Result<i32> = Err(validation_error("age", "年龄不能为负数"));
503
504 let contextual_result = with_context(result, "user_id", "12345");
505 assert!(contextual_result.is_err());
506
507 let error = contextual_result.unwrap_err();
508 assert_eq!(error.context().get_context("user_id"), Some("12345"));
511 }
512
513 #[test]
514 fn test_sdk_result_conversion() {
515 let core_result: openlark_core::SDKResult<String> = Ok("success".to_string());
517 let client_result: Result<String> = from_sdk_result(core_result);
518 assert!(client_result.is_ok());
519 assert_eq!(client_result.unwrap(), "success");
520
521 let core_result: openlark_core::SDKResult<String> = Err(network_error("网络错误"));
523 let client_result: Result<String> = from_sdk_result(core_result);
524 assert!(client_result.is_err());
525 assert!(client_result.unwrap_err().is_network_error());
526 }
527
528 #[test]
529 fn test_api_error_function() {
530 let error = api_error(
531 500,
532 "/api/test",
533 "服务器内部错误",
534 Some("req-456".to_string()),
535 );
536 assert!(error.is_api_error());
537 let analyzer = ErrorAnalyzer::new(&error);
538 let report = analyzer.detailed_report();
539 assert!(report.contains("服务器内部错误"));
540 }
541
542 #[test]
543 fn test_validation_error_function() {
544 let error = validation_error("field_name", "字段值为空");
545 assert!(error.is_validation_error());
546 let analyzer = ErrorAnalyzer::new(&error);
547 let user_msg = analyzer.user_friendly_with_suggestion();
548 assert!(user_msg.contains("建议"));
549 }
550
551 #[test]
552 fn test_configuration_error_function() {
553 let error = configuration_error("配置文件缺失");
554 assert!(error.is_config_error());
555 }
556
557 #[test]
558 fn test_serialization_error_function() {
559 let error = serialization_error("JSON解析失败");
560 assert!(error.is_serialization_error());
561 }
562
563 #[test]
564 fn test_business_error_function() {
565 let error = business_error("ERR_001", "业务规则验证失败");
566 assert!(error.is_business_error());
567 }
568
569 #[test]
570 fn test_timeout_error_function() {
571 let error = timeout_error("数据库查询超时");
572 assert!(error.is_timeout_error());
573 assert!(error.is_retryable());
574 }
575
576 #[test]
577 fn test_rate_limit_error_function() {
578 let error = rate_limit_error(Some(60));
579 assert!(error.is_rate_limited());
580 }
581
582 #[test]
583 fn test_service_unavailable_error_function() {
584 let error = service_unavailable_error("支付服务");
585 assert!(error.is_service_unavailable_error());
586 }
587
588 #[test]
589 fn test_internal_error_function() {
590 let error = internal_error("系统内部错误");
591 assert!(!error.is_user_error());
592 }
593
594 #[test]
595 fn test_token_invalid_error_function() {
596 let error = token_invalid_error("token格式不正确");
597 assert!(error.is_auth_error());
598 }
599
600 #[test]
601 fn test_token_expired_error_function() {
602 let error = token_expired_error("token已过期");
603 assert!(error.is_auth_error());
604 }
605
606 #[test]
607 fn test_permission_missing_error_function() {
608 let scopes = vec!["read:user", "write:docs"];
609 let error = permission_missing_error(&scopes);
610 assert!(error.is_auth_error());
611 }
612
613 #[test]
614 fn test_sso_token_invalid_error_function() {
615 let error = sso_token_invalid_error("SSO令牌无效");
616 assert!(error.is_auth_error());
617 }
618
619 #[test]
620 fn test_user_identity_invalid_error_function() {
621 let error = user_identity_invalid_error("用户身份标识非法");
622 assert!(error.is_auth_error());
623 }
624
625 #[test]
626 fn test_from_feishu_response_function() {
627 let error = from_feishu_response(
628 99991677,
629 "/api/test",
630 "token过期",
631 Some("req-789".to_string()),
632 );
633 assert!(!error.to_string().is_empty());
635 let error2 = from_feishu_response(400, "/api/test", "参数错误", None);
636 assert!(!error2.to_string().is_empty());
637 }
638
639 #[test]
640 fn test_registry_error_conversion() {
641 let registry_err = crate::registry::RegistryError::ServiceNotFound {
642 name: "test".to_string(),
643 };
644 let error: Error = registry_err.into();
645 assert!(!error.is_user_error());
646 }
647
648 #[test]
649 fn test_error_analyzer_log_summary() {
650 let error = network_error("连接超时");
651 let analyzer = ErrorAnalyzer::new(&error);
652 let summary = analyzer.log_summary();
653 assert!(summary.contains("Network"));
654 assert!(summary.contains("可重试") || summary.contains("不可重试"));
655 }
656
657 #[test]
658 fn test_error_analyzer_user_friendly() {
659 let error = api_error(404, "/users/123", "用户不存在", None);
660 let analyzer = ErrorAnalyzer::new(&error);
661 let friendly = analyzer.user_friendly_with_suggestion();
662 assert!(friendly.contains("建议"));
663 assert!(friendly.contains("可以尝试"));
664 }
665
666 #[test]
667 fn test_with_operation_context() {
668 let result: Result<i32> = Err(network_error("网络错误"));
669 let contextual_result = with_operation_context(result, "test_operation", "TestComponent");
670 assert!(contextual_result.is_err());
671 let error = contextual_result.unwrap_err();
672 assert_eq!(
673 error.context().get_context("operation"),
674 Some("test_operation")
675 );
676 assert_eq!(
677 error.context().get_context("component"),
678 Some("TestComponent")
679 );
680 }
681
682 #[test]
683 fn test_with_operation_context_updates_timeout_operation_field() {
684 use std::time::Duration;
685
686 let result: Result<i32> = Err(openlark_core::error::timeout_error(
687 Duration::from_secs(3),
688 Some("old_operation".to_string()),
689 ));
690
691 let contextual_result = with_operation_context(result, "new_operation", "ClientLayer");
692 assert!(contextual_result.is_err());
693
694 match contextual_result.unwrap_err() {
695 CoreError::Timeout {
696 operation, ref ctx, ..
697 } => {
698 assert_eq!(operation.as_deref(), Some("new_operation"));
699 assert_eq!(ctx.operation(), Some("new_operation"));
700 assert_eq!(ctx.component(), Some("ClientLayer"));
701 }
702 other => panic!("expected timeout error, got {:?}", other.error_type()),
703 }
704 }
705
706 #[test]
707 fn test_all_error_types_suggestion() {
708 let error_types = vec![
709 (network_error("test"), "检查网络连接"),
710 (authentication_error("test"), "验证应用凭据"),
711 (api_error(500, "/test", "test", None), "检查API参数"),
712 (validation_error("field", "test"), "验证输入参数"),
713 (configuration_error("test"), "检查应用配置"),
714 (serialization_error("test"), "确认数据格式"),
715 (business_error("code", "test"), "确认业务逻辑"),
716 (timeout_error("test"), "增加超时时间"),
717 (rate_limit_error(None), "稍后重试"),
718 (service_unavailable_error("svc"), "稍后重试"),
719 (internal_error("test"), "联系技术支持"),
720 ];
721
722 for (error, expected_keyword) in error_types {
723 let suggestion = error.suggestion();
724 assert!(
725 suggestion.contains(expected_keyword) || !suggestion.is_empty(),
726 "Error type {:?} should have meaningful suggestion",
727 error.error_type()
728 );
729 }
730 }
731
732 #[test]
733 fn test_all_error_types_recovery_steps() {
734 let error_types = vec![
735 network_error("test"),
736 authentication_error("test"),
737 api_error(500, "/test", "test", None),
738 validation_error("field", "test"),
739 configuration_error("test"),
740 serialization_error("test"),
741 business_error("code", "test"),
742 timeout_error("test"),
743 rate_limit_error(None),
744 service_unavailable_error("svc"),
745 internal_error("test"),
746 ];
747
748 for error in error_types {
749 let steps = error.recovery_steps();
750 assert!(
751 !steps.is_empty(),
752 "Error type {:?} should have recovery steps",
753 error.error_type()
754 );
755 assert!(
756 steps.len() >= 3,
757 "Error type {:?} should have at least 3 recovery steps",
758 error.error_type()
759 );
760 }
761 }
762}