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