Skip to main content

openlark_core/error/
core.rs

1//! CoreError:基于 thiserror 的企业级错误处理
2//!
3//! 目标:展示单一映射来源(ErrorCode),简化字段、统一严重度/可重试策略,
4//! 并保留可序列化的 ErrorRecord 供观测与分析使用。
5
6use std::{borrow::Cow, time::Duration};
7
8use super::{
9    codes::ErrorCode,
10    context::ErrorContext,
11    traits::{ErrorSeverity, ErrorTrait, ErrorType},
12};
13use serde::Serialize;
14use thiserror::Error;
15
16// ============================================================================
17// 重试与恢复策略
18// ============================================================================
19
20/// 重试策略(供 CoreError 使用)
21#[derive(Debug, Clone)]
22pub struct RetryPolicy {
23    /// 最大重试次数
24    pub max_retries: u32,
25    /// 寏次重试基础延迟
26    pub base_delay: Duration,
27    /// 退避因子
28    pub backoff_factor: f64,
29    /// 最大延迟
30    pub max_delay: Option<Duration>,
31}
32
33impl RetryPolicy {
34    /// 创建不重试策略
35    pub fn no_retry() -> Self {
36        Self {
37            max_retries: 0,
38            base_delay: Duration::from_secs(1),
39            backoff_factor: 1.0,
40            max_delay: None,
41        }
42    }
43
44    /// 创建固定延迟重试策略
45    pub fn fixed(max_retries: u32, delay: Duration) -> Self {
46        Self {
47            max_retries,
48            base_delay: delay,
49            backoff_factor: 1.0,
50            max_delay: Some(delay),
51        }
52    }
53
54    /// 创建指数退避重试策略
55    pub fn exponential(max_retries: u32, base_delay: Duration) -> Self {
56        Self {
57            max_retries,
58            base_delay,
59            backoff_factor: 2.0,
60            max_delay: Some(Duration::from_secs(300)), // 5分钟最大延迟
61        }
62    }
63
64    /// 是否可重试
65    pub fn is_retryable(&self) -> bool {
66        self.max_retries > 0
67    }
68
69    /// 计算重试延迟
70    pub fn retry_delay(&self, attempt: u32) -> Option<Duration> {
71        if attempt >= self.max_retries {
72            return None;
73        }
74
75        let delay = if self.backoff_factor == 1.0 {
76            self.base_delay
77        } else {
78            let seconds = self.base_delay.as_secs_f64() * self.backoff_factor.powi(attempt as i32);
79            Duration::from_secs_f64(seconds)
80        };
81
82        Some(self.max_delay.map_or(delay, |max| delay.min(max)))
83    }
84
85    /// 直接获取延迟(超过最大重试返回0)
86    pub fn delay(&self, attempt: u32) -> Duration {
87        self.retry_delay(attempt).unwrap_or(Duration::ZERO)
88    }
89
90    /// 是否使用指数退避
91    pub fn use_exponential_backoff(&self) -> bool {
92        self.backoff_factor > 1.0
93    }
94
95    /// 最大重试次数(保持旧接口)
96    pub fn max_retries(&self) -> u32 {
97        self.max_retries
98    }
99}
100
101impl Default for RetryPolicy {
102    fn default() -> Self {
103        Self::exponential(3, Duration::from_secs(1))
104    }
105}
106
107/// 恢复策略
108#[derive(Debug, Clone)]
109pub enum RecoveryStrategy {
110    /// 重试并使用指数退避
111    RetryWithBackoff,
112    /// 验证后重试
113    ValidateAndRetry,
114    /// 重新认证
115    Reauthenticate,
116    /// 请求权限
117    RequestPermission,
118    /// 手动干预
119    ManualIntervention,
120    /// 延迟重试
121    RetryWithDelay,
122}
123
124type AnyError = Box<dyn std::error::Error + Send + Sync>;
125
126/// 枚举化的构建器目标类型
127#[derive(Debug, Clone, Copy)]
128pub enum BuilderKind {
129    /// 网络错误
130    Network,
131    /// 认证错误
132    Authentication,
133    /// API 错误
134    Api,
135    /// 验证错误
136    Validation,
137    /// 配置错误
138    Configuration,
139    /// 序列化错误
140    Serialization,
141    /// 业务错误
142    Business,
143    /// 超时错误
144    Timeout,
145    /// 限流错误
146    RateLimit,
147    /// 服务不可用错误
148    ServiceUnavailable,
149    /// 内部错误
150    Internal,
151}
152
153/// 统一的错误构建器,避免直接依赖枚举内部字段
154#[derive(Debug)]
155pub struct ErrorBuilder {
156    /// 构建器目标类型
157    kind: BuilderKind,
158    /// 错误消息
159    message: Option<String>,
160    /// 错误码
161    code: Option<ErrorCode>,
162    /// HTTP 状态码
163    status: Option<u16>,
164    /// API 端点
165    endpoint: Option<String>,
166    /// 验证字段名
167    field: Option<String>,
168    /// 源错误
169    source: Option<AnyError>,
170    /// 重试策略
171    policy: Option<RetryPolicy>,
172    /// 错误上下文
173    ctx: ErrorContext,
174    /// 持续时间
175    duration: Option<Duration>,
176    /// 操作名
177    operation: Option<String>,
178    /// 限流限制
179    limit: Option<u32>,
180    /// 限流窗口
181    window: Option<Duration>,
182    /// 重置时间
183    reset_after: Option<Duration>,
184    /// 服务名
185    service: Option<String>,
186    /// 重试等待时间
187    retry_after: Option<Duration>,
188}
189
190impl ErrorBuilder {
191    /// 创建指定类型的错误构建器
192    pub fn new(kind: BuilderKind) -> Self {
193        Self {
194            kind,
195            message: None,
196            code: None,
197            status: None,
198            endpoint: None,
199            field: None,
200            source: None,
201            policy: None,
202            ctx: ErrorContext::new(),
203            duration: None,
204            operation: None,
205            limit: None,
206            window: None,
207            reset_after: None,
208            service: None,
209            retry_after: None,
210        }
211    }
212
213    /// 设置错误消息
214    pub fn message(mut self, msg: impl Into<String>) -> Self {
215        self.message = Some(msg.into());
216        self
217    }
218
219    /// 设置错误码
220    pub fn code(mut self, code: ErrorCode) -> Self {
221        self.code = Some(code);
222        self
223    }
224
225    /// 设置 HTTP 状态码
226    pub fn status(mut self, status: u16) -> Self {
227        self.status = Some(status);
228        self
229    }
230
231    /// 设置 API 端点
232    pub fn endpoint(mut self, endpoint: impl Into<String>) -> Self {
233        self.endpoint = Some(endpoint.into());
234        self
235    }
236
237    /// 设置验证字段名
238    pub fn field(mut self, field: impl Into<String>) -> Self {
239        self.field = Some(field.into());
240        self
241    }
242
243    /// 设置源错误
244    pub fn source<E: std::error::Error + Send + Sync + 'static>(mut self, err: E) -> Self {
245        self.source = Some(Box::new(err));
246        self
247    }
248
249    /// 设置重试策略
250    pub fn policy(mut self, policy: RetryPolicy) -> Self {
251        self.policy = Some(policy);
252        self
253    }
254
255    /// 设置请求 ID
256    pub fn request_id(mut self, req: impl Into<String>) -> Self {
257        self.ctx.set_request_id(req);
258        self
259    }
260
261    /// 设置操作名称
262    pub fn operation(mut self, op: impl Into<String>) -> Self {
263        let op = op.into();
264        self.operation = Some(op.clone());
265        self.ctx.set_operation(op);
266        self
267    }
268
269    /// 设置组件名称
270    pub fn component(mut self, comp: impl Into<String>) -> Self {
271        self.ctx.set_component(comp);
272        self
273    }
274
275    /// 设置用户友好的错误消息
276    pub fn user_message(mut self, msg: impl Into<String>) -> Self {
277        self.ctx.set_user_message(msg);
278        self
279    }
280
281    /// 添加上下文键值对
282    pub fn context(mut self, key: impl Into<String>, val: impl Into<String>) -> Self {
283        self.ctx.add_context(key, val);
284        self
285    }
286
287    /// 设置超时时长
288    pub fn duration(mut self, duration: Duration) -> Self {
289        self.duration = Some(duration);
290        self
291    }
292
293    /// 设置限流次数
294    pub fn limit(mut self, limit: u32) -> Self {
295        self.limit = Some(limit);
296        self
297    }
298
299    /// 设置限流时间窗口
300    pub fn window(mut self, window: Duration) -> Self {
301        self.window = Some(window);
302        self
303    }
304
305    /// 设置限流重置时间
306    pub fn reset_after(mut self, reset: Duration) -> Self {
307        self.reset_after = Some(reset);
308        self
309    }
310
311    /// 设置服务名
312    pub fn service(mut self, service: impl Into<String>) -> Self {
313        self.service = Some(service.into());
314        self
315    }
316
317    /// 设置重试等待时间
318    pub fn retry_after(mut self, retry_after: Duration) -> Self {
319        self.retry_after = Some(retry_after);
320        self
321    }
322
323    /// 构建 CoreError 实例
324    pub fn build(self) -> CoreError {
325        let msg = self.message.unwrap_or_else(|| "unknown error".to_string());
326        match self.kind {
327            BuilderKind::Network => CoreError::Network(Box::new(NetworkError {
328                message: msg,
329                source: self.source,
330                policy: self.policy.unwrap_or_default(),
331                ctx: Box::new(self.ctx),
332            })),
333            BuilderKind::Authentication => CoreError::Authentication {
334                message: msg,
335                code: self.code.unwrap_or(ErrorCode::AuthenticationFailed),
336                ctx: Box::new(self.ctx),
337            },
338            BuilderKind::Api => {
339                let status = self.status.unwrap_or(500);
340                CoreError::Api(Box::new(ApiError {
341                    status,
342                    endpoint: self
343                        .endpoint
344                        .unwrap_or_else(|| "unknown".to_string())
345                        .into(),
346                    message: msg,
347                    source: self.source,
348                    code: self
349                        .code
350                        .unwrap_or_else(|| ErrorCode::from_http_status(status)),
351                    ctx: Box::new(self.ctx),
352                }))
353            }
354            BuilderKind::Validation => CoreError::Validation {
355                field: self.field.unwrap_or_else(|| "field".to_string()).into(),
356                message: msg,
357                code: self.code.unwrap_or(ErrorCode::ValidationError),
358                ctx: Box::new(self.ctx),
359            },
360            BuilderKind::Configuration => CoreError::Configuration {
361                message: msg,
362                code: self.code.unwrap_or(ErrorCode::ConfigurationError),
363                ctx: Box::new(self.ctx),
364            },
365            BuilderKind::Serialization => CoreError::Serialization {
366                message: msg,
367                source: self.source,
368                code: self.code.unwrap_or(ErrorCode::SerializationError),
369                ctx: Box::new(self.ctx),
370            },
371            BuilderKind::Business => CoreError::Business {
372                code: self.code.unwrap_or(ErrorCode::BusinessError),
373                message: msg,
374                ctx: Box::new(self.ctx),
375            },
376            BuilderKind::Timeout => CoreError::Timeout {
377                duration: self.duration.unwrap_or_default(),
378                operation: self.operation,
379                ctx: Box::new(self.ctx),
380            },
381            BuilderKind::RateLimit => CoreError::RateLimit {
382                limit: self.limit.unwrap_or(0),
383                window: self.window.unwrap_or(Duration::from_secs(1)),
384                reset_after: self.reset_after,
385                code: self.code.unwrap_or(ErrorCode::RateLimitExceeded),
386                ctx: Box::new(self.ctx),
387            },
388            BuilderKind::ServiceUnavailable => CoreError::ServiceUnavailable {
389                service: self.service.unwrap_or_else(|| "service".to_string()).into(),
390                retry_after: self.retry_after,
391                code: self.code.unwrap_or(ErrorCode::ServiceUnavailable),
392                ctx: Box::new(self.ctx),
393            },
394            BuilderKind::Internal => CoreError::Internal {
395                code: self.code.unwrap_or(ErrorCode::InternalError),
396                message: msg,
397                source: self.source,
398                ctx: Box::new(self.ctx),
399            },
400        }
401    }
402}
403
404/// 轻量版核心错误
405#[non_exhaustive]
406#[derive(Debug, Error)]
407pub enum CoreError {
408    /// 网络错误
409    #[error("网络错误: {0}")]
410    Network(Box<NetworkError>),
411
412    /// 认证错误
413    #[error("认证失败: {message}")]
414    Authentication {
415        /// 错误消息
416        message: String,
417        /// 错误码
418        code: ErrorCode,
419        /// 错误上下文
420        ctx: Box<ErrorContext>,
421    },
422
423    /// API 错误
424    #[error("API错误 {0}")]
425    Api(Box<ApiError>),
426
427    /// 验证错误
428    #[error("验证错误 {field}: {message}")]
429    Validation {
430        /// 字段名
431        field: Cow<'static, str>,
432        /// 错误消息
433        message: String,
434        /// 错误码
435        code: ErrorCode,
436        /// 错误上下文
437        ctx: Box<ErrorContext>,
438    },
439
440    /// 配置错误
441    #[error("配置错误: {message}")]
442    Configuration {
443        /// 错误消息
444        message: String,
445        /// 错误码
446        code: ErrorCode,
447        /// 错误上下文
448        ctx: Box<ErrorContext>,
449    },
450
451    /// 序列化错误
452    #[error("序列化错误: {message}")]
453    Serialization {
454        /// 错误消息
455        message: String,
456        /// 源错误
457        #[source]
458        source: Option<AnyError>,
459        /// 错误码
460        code: ErrorCode,
461        /// 错误上下文
462        ctx: Box<ErrorContext>,
463    },
464
465    /// 业务错误
466    #[error("业务错误 {code:?}: {message}")]
467    Business {
468        /// 错误码
469        code: ErrorCode,
470        /// 错误消息
471        message: String,
472        /// 错误上下文
473        ctx: Box<ErrorContext>,
474    },
475
476    /// 超时错误
477    #[error("超时 {operation:?} after {duration:?}")]
478    Timeout {
479        /// 超时时长
480        duration: Duration,
481        /// 操作名
482        operation: Option<String>,
483        /// 错误上下文
484        ctx: Box<ErrorContext>,
485    },
486
487    /// 限流错误
488    #[error("限流: {limit} 次/{window:?}")]
489    RateLimit {
490        /// 限制次数
491        limit: u32,
492        /// 时间窗口
493        window: Duration,
494        /// 重置时间
495        reset_after: Option<Duration>,
496        /// 错误码
497        code: ErrorCode,
498        /// 错误上下文
499        ctx: Box<ErrorContext>,
500    },
501
502    /// 服务不可用错误
503    #[error("服务不可用: {service}")]
504    ServiceUnavailable {
505        /// 服务名
506        service: Cow<'static, str>,
507        /// 重试等待时间
508        retry_after: Option<Duration>,
509        /// 错误码
510        code: ErrorCode,
511        /// 错误上下文
512        ctx: Box<ErrorContext>,
513    },
514
515    /// 内部错误
516    #[error("内部错误 {code:?}: {message}")]
517    Internal {
518        /// 错误码
519        code: ErrorCode,
520        /// 错误消息
521        message: String,
522        /// 源错误
523        #[source]
524        source: Option<AnyError>,
525        /// 错误上下文
526        ctx: Box<ErrorContext>,
527    },
528}
529
530/// 网络错误
531#[derive(Debug)]
532pub struct NetworkError {
533    /// 错误消息
534    pub message: String,
535    /// 源错误
536    pub source: Option<AnyError>,
537    /// 重试策略
538    pub policy: RetryPolicy,
539    /// 错误上下文
540    pub ctx: Box<ErrorContext>,
541}
542
543impl std::fmt::Display for NetworkError {
544    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
545        write!(f, "{}", self.message)
546    }
547}
548
549/// API 错误
550#[derive(Debug)]
551pub struct ApiError {
552    /// HTTP 状态码
553    pub status: u16,
554    /// API 端点
555    pub endpoint: Cow<'static, str>,
556    /// 错误消息
557    pub message: String,
558    /// 源错误
559    pub source: Option<AnyError>,
560    /// 错误码
561    pub code: ErrorCode,
562    /// 错误上下文
563    pub ctx: Box<ErrorContext>,
564}
565
566impl std::fmt::Display for ApiError {
567    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
568        write!(f, "{} {}: {}", self.status, self.endpoint, self.message)
569    }
570}
571
572impl Clone for CoreError {
573    fn clone(&self) -> Self {
574        match self {
575            Self::Network(net) => Self::Network(Box::new(NetworkError {
576                message: net.message.clone(),
577                source: None, // 源错误不可克隆,丢弃以便重试记录
578                policy: net.policy.clone(),
579                ctx: net.ctx.clone(),
580            })),
581            Self::Authentication { message, code, ctx } => Self::Authentication {
582                message: message.clone(),
583                code: *code,
584                ctx: ctx.clone(),
585            },
586            Self::Api(api) => Self::Api(Box::new(ApiError {
587                status: api.status,
588                endpoint: api.endpoint.clone(),
589                message: api.message.clone(),
590                source: None,
591                code: api.code,
592                ctx: api.ctx.clone(),
593            })),
594            Self::Validation {
595                field,
596                message,
597                code,
598                ctx,
599            } => Self::Validation {
600                field: field.clone(),
601                message: message.clone(),
602                code: *code,
603                ctx: ctx.clone(),
604            },
605            Self::Configuration { message, code, ctx } => Self::Configuration {
606                message: message.clone(),
607                code: *code,
608                ctx: ctx.clone(),
609            },
610            Self::Serialization {
611                message, code, ctx, ..
612            } => Self::Serialization {
613                message: message.clone(),
614                source: None,
615                code: *code,
616                ctx: ctx.clone(),
617            },
618            Self::Business { code, message, ctx } => Self::Business {
619                code: *code,
620                message: message.clone(),
621                ctx: ctx.clone(),
622            },
623            Self::Timeout {
624                duration,
625                operation,
626                ctx,
627            } => Self::Timeout {
628                duration: *duration,
629                operation: operation.clone(),
630                ctx: ctx.clone(),
631            },
632            Self::RateLimit {
633                limit,
634                window,
635                reset_after,
636                code,
637                ctx,
638            } => Self::RateLimit {
639                limit: *limit,
640                window: *window,
641                reset_after: *reset_after,
642                code: *code,
643                ctx: ctx.clone(),
644            },
645            Self::ServiceUnavailable {
646                service,
647                retry_after,
648                code,
649                ctx,
650            } => Self::ServiceUnavailable {
651                service: service.clone(),
652                retry_after: *retry_after,
653                code: *code,
654                ctx: ctx.clone(),
655            },
656            Self::Internal {
657                code, message, ctx, ..
658            } => Self::Internal {
659                code: *code,
660                message: message.clone(),
661                source: None,
662                ctx: ctx.clone(),
663            },
664        }
665    }
666}
667
668impl CoreError {
669    /// 统一构建器入口
670    pub fn builder(kind: BuilderKind) -> ErrorBuilder {
671        ErrorBuilder::new(kind)
672    }
673
674    /// 网络错误构建器
675    pub fn network_builder() -> ErrorBuilder {
676        ErrorBuilder::new(BuilderKind::Network)
677    }
678
679    /// API 错误构建器
680    pub fn api_builder() -> ErrorBuilder {
681        ErrorBuilder::new(BuilderKind::Api)
682    }
683
684    /// 验证错误构建器
685    pub fn validation_builder() -> ErrorBuilder {
686        ErrorBuilder::new(BuilderKind::Validation)
687    }
688
689    /// 认证错误构建器
690    pub fn authentication_builder() -> ErrorBuilder {
691        ErrorBuilder::new(BuilderKind::Authentication)
692    }
693
694    /// 业务错误构建器
695    pub fn business_builder() -> ErrorBuilder {
696        ErrorBuilder::new(BuilderKind::Business)
697    }
698
699    /// 简单网络错误(无 source)
700    pub fn network_msg(message: impl Into<String>) -> Self {
701        network_error(message)
702    }
703
704    /// 简单认证错误
705    pub fn authentication(message: impl Into<String>) -> Self {
706        authentication_error(message)
707    }
708
709    /// 简单 API 错误(便于兼容旧 CoreError::api_error)
710    pub fn api_error(
711        status: i32,
712        endpoint: impl Into<String>,
713        message: impl Into<String>,
714        request_id: Option<impl Into<String>>,
715    ) -> Self {
716        api_error(
717            status as u16,
718            endpoint,
719            message,
720            request_id.map(|id| id.into()),
721        )
722    }
723
724    /// 仅带 message 的验证错误(默认字段 general)
725    pub fn validation_msg(message: impl Into<String>) -> Self {
726        validation_error("general", message)
727    }
728
729    /// 用户可读 message(兼容旧 API)
730    pub fn message(&self) -> String {
731        self.to_string()
732    }
733
734    /// 错误类型别名(兼容旧 kind)
735    pub fn kind(&self) -> ErrorType {
736        self.error_type()
737    }
738
739    /// 直接判断 API 错误(便捷别名)
740    pub fn is_api_error(&self) -> bool {
741        matches!(self, Self::Api(_))
742    }
743
744    // === 兼容旧 CoreError 的工厂 ===
745    /// 验证错误
746    pub fn validation(field: impl Into<String>, message: impl Into<String>) -> Self {
747        let mut ctx = ErrorContext::new();
748        let field = field.into();
749        ctx.add_context("field", field.clone());
750        Self::Validation {
751            field: field.into(),
752            message: message.into(),
753            code: ErrorCode::ValidationError,
754            ctx: Box::new(ctx),
755        }
756    }
757
758    /// API 数据错误
759    pub fn api_data_error(message: impl Into<String>) -> Self {
760        Self::Api(Box::new(ApiError {
761            status: 500,
762            endpoint: "data_error".into(),
763            message: format!("no data: {}", message.into()),
764            source: None,
765            code: ErrorCode::InternalServerError,
766            ctx: Box::new(ErrorContext::new()),
767        }))
768    }
769
770    /// 获取错误码
771    pub fn code(&self) -> ErrorCode {
772        match self {
773            Self::Network(_) => ErrorCode::NetworkConnectionFailed,
774            Self::Authentication { code, .. } => *code,
775            Self::Api(api) => api.code,
776            Self::Validation { code, .. } => *code,
777            Self::Configuration { code, .. } => *code,
778            Self::Serialization { code, .. } => *code,
779            Self::Business { code, .. } => *code,
780            Self::Timeout { .. } => ErrorCode::NetworkTimeout,
781            Self::RateLimit { code, .. } => *code,
782            Self::ServiceUnavailable { code, .. } => *code,
783            Self::Internal { code, .. } => *code,
784        }
785    }
786
787    /// 获取错误严重程度
788    pub fn severity(&self) -> ErrorSeverity {
789        self.code().severity()
790    }
791
792    /// 是否可重试
793    pub fn is_retryable(&self) -> bool {
794        match self {
795            Self::Network(net) => net.policy.is_retryable(),
796            Self::Api(api) => matches!(api.status, 429 | 500..=599),
797            Self::Timeout { .. } => self.code().is_retryable(),
798            Self::RateLimit { .. } => self.code().is_retryable(),
799            Self::ServiceUnavailable { .. } => self.code().is_retryable(),
800            Self::Internal { .. } => self.code().is_retryable(),
801            _ => false,
802        }
803    }
804
805    /// 获取重试延迟
806    pub fn retry_delay(&self, attempt: u32) -> Option<Duration> {
807        match self {
808            Self::Network(net) => net.policy.retry_delay(attempt),
809            Self::RateLimit { window, .. } => Some(*window),
810            Self::ServiceUnavailable { retry_after, .. } => *retry_after,
811            Self::Api(api) if matches!(api.status, 429 | 500..=599) => {
812                Some(Duration::from_secs(1 << attempt.min(5)))
813            }
814            _ => None,
815        }
816    }
817
818    /// 获取错误上下文
819    pub fn ctx(&self) -> &ErrorContext {
820        match self {
821            Self::Network(net) => &net.ctx,
822            Self::Api(api) => &api.ctx,
823            Self::Authentication { ctx, .. }
824            | Self::Validation { ctx, .. }
825            | Self::Configuration { ctx, .. }
826            | Self::Serialization { ctx, .. }
827            | Self::Business { ctx, .. }
828            | Self::Timeout { ctx, .. }
829            | Self::RateLimit { ctx, .. }
830            | Self::ServiceUnavailable { ctx, .. }
831            | Self::Internal { ctx, .. } => ctx,
832        }
833    }
834
835    /// 修改错误上下文
836    pub fn map_context(self, f: impl FnOnce(&mut ErrorContext)) -> Self {
837        match self {
838            Self::Network(mut net) => {
839                f(net.ctx.as_mut());
840                Self::Network(net)
841            }
842            Self::Authentication {
843                message,
844                code,
845                mut ctx,
846            } => {
847                f(ctx.as_mut());
848                Self::Authentication { message, code, ctx }
849            }
850            Self::Api(mut api) => {
851                f(api.ctx.as_mut());
852                Self::Api(api)
853            }
854            Self::Validation {
855                field,
856                message,
857                code,
858                mut ctx,
859            } => {
860                f(ctx.as_mut());
861                Self::Validation {
862                    field,
863                    message,
864                    code,
865                    ctx,
866                }
867            }
868            Self::Configuration {
869                message,
870                code,
871                mut ctx,
872            } => {
873                f(ctx.as_mut());
874                Self::Configuration { message, code, ctx }
875            }
876            Self::Serialization {
877                message,
878                source,
879                code,
880                mut ctx,
881            } => {
882                f(ctx.as_mut());
883                Self::Serialization {
884                    message,
885                    source,
886                    code,
887                    ctx,
888                }
889            }
890            Self::Business {
891                code,
892                message,
893                mut ctx,
894            } => {
895                f(ctx.as_mut());
896                Self::Business { code, message, ctx }
897            }
898            Self::Timeout {
899                duration,
900                operation,
901                mut ctx,
902            } => {
903                f(ctx.as_mut());
904                Self::Timeout {
905                    duration,
906                    operation,
907                    ctx,
908                }
909            }
910            Self::RateLimit {
911                limit,
912                window,
913                reset_after,
914                code,
915                mut ctx,
916            } => {
917                f(ctx.as_mut());
918                Self::RateLimit {
919                    limit,
920                    window,
921                    reset_after,
922                    code,
923                    ctx,
924                }
925            }
926            Self::ServiceUnavailable {
927                service,
928                retry_after,
929                code,
930                mut ctx,
931            } => {
932                f(ctx.as_mut());
933                Self::ServiceUnavailable {
934                    service,
935                    retry_after,
936                    code,
937                    ctx,
938                }
939            }
940            Self::Internal {
941                code,
942                message,
943                source,
944                mut ctx,
945            } => {
946                f(ctx.as_mut());
947                Self::Internal {
948                    code,
949                    message,
950                    source,
951                    ctx,
952                }
953            }
954        }
955    }
956
957    /// 添加上下文键值对
958    pub fn with_context_kv(self, key: impl Into<String>, value: impl Into<String>) -> Self {
959        let key = key.into();
960        let value = value.into();
961        self.map_context(|ctx| {
962            ctx.add_context(key, value);
963        })
964    }
965
966    /// 添加操作和组件信息
967    pub fn with_operation(
968        self,
969        operation: impl Into<String>,
970        component: impl Into<String>,
971    ) -> Self {
972        let operation = operation.into();
973        let component = component.into();
974
975        let mapped = self.map_context(|ctx| {
976            ctx.set_operation(operation.clone())
977                .set_component(component.clone())
978                .add_context("operation", operation.clone())
979                .add_context("component", component.clone());
980        });
981
982        match mapped {
983            Self::Timeout { duration, ctx, .. } => Self::Timeout {
984                duration,
985                operation: Some(operation),
986                ctx,
987            },
988            other => other,
989        }
990    }
991
992    /// 观测记录(可序列化)——供日志/指标/告警统一使用
993    pub fn record(&self) -> ErrorRecord {
994        ErrorRecord::from(self)
995    }
996
997    // === 快速构造器(示例) ===
998    /// 网络错误
999    pub fn network<E>(source: E, ctx: ErrorContext) -> Self
1000    where
1001        E: std::error::Error + Send + Sync + 'static,
1002    {
1003        Self::Network(Box::new(NetworkError {
1004            message: "网络连接失败".to_string(),
1005            source: Some(Box::new(source)),
1006            policy: RetryPolicy::default(),
1007            ctx: Box::new(ctx),
1008        }))
1009    }
1010
1011    /// API 错误
1012    pub fn api(
1013        status: u16,
1014        endpoint: impl Into<Cow<'static, str>>,
1015        message: impl Into<String>,
1016        ctx: ErrorContext,
1017    ) -> Self {
1018        Self::Api(Box::new(ApiError {
1019            status,
1020            endpoint: endpoint.into(),
1021            message: message.into(),
1022            source: None,
1023            code: ErrorCode::from_http_status(status),
1024            ctx: Box::new(ctx),
1025        }))
1026    }
1027}
1028
1029/// 统一的可观测记录
1030#[derive(Debug, Serialize)]
1031#[serde_with::skip_serializing_none]
1032pub struct ErrorRecord {
1033    /// 错误码
1034    pub code: ErrorCode,
1035    /// 严重程度
1036    pub severity: ErrorSeverity,
1037    /// 是否可重试
1038    pub retryable: bool,
1039    /// 重试延迟(毫秒)
1040    pub retry_delay_ms: Option<u64>,
1041    /// 错误消息
1042    pub message: String,
1043    /// 上下文
1044    pub context: std::collections::HashMap<String, String>,
1045    /// 请求 ID
1046    pub request_id: Option<String>,
1047    /// 操作名称
1048    pub operation: Option<String>,
1049    /// 组件名称
1050    pub component: Option<String>,
1051}
1052
1053impl From<&CoreError> for ErrorRecord {
1054    fn from(err: &CoreError) -> Self {
1055        let ctx = err.ctx();
1056        Self {
1057            code: err.code(),
1058            severity: err.severity(),
1059            retryable: err.is_retryable(),
1060            retry_delay_ms: err.retry_delay(0).map(|d| d.as_millis() as u64),
1061            message: err.to_string(),
1062            context: ctx.all_context().clone(),
1063            request_id: ctx.request_id().map(|s| s.to_string()),
1064            operation: ctx.operation().map(|s| s.to_string()),
1065            component: ctx.component().map(|s| s.to_string()),
1066        }
1067    }
1068}
1069
1070impl From<reqwest::Error> for CoreError {
1071    fn from(source: reqwest::Error) -> Self {
1072        Self::Network(Box::new(NetworkError {
1073            message: source.to_string(),
1074            source: Some(Box::new(source)),
1075            policy: RetryPolicy::default(),
1076            ctx: Box::new(ErrorContext::new()),
1077        }))
1078    }
1079}
1080
1081impl From<serde_json::Error> for CoreError {
1082    fn from(source: serde_json::Error) -> Self {
1083        Self::Serialization {
1084            message: format!("JSON序列化错误: {}", source),
1085            source: Some(Box::new(source)),
1086            code: ErrorCode::SerializationError,
1087            ctx: Box::new(ErrorContext::new()),
1088        }
1089    }
1090}
1091
1092impl ErrorTrait for CoreError {
1093    fn severity(&self) -> ErrorSeverity {
1094        self.severity()
1095    }
1096
1097    fn is_retryable(&self) -> bool {
1098        self.is_retryable()
1099    }
1100
1101    fn retry_delay(&self, attempt: u32) -> Option<Duration> {
1102        self.retry_delay(attempt)
1103    }
1104
1105    fn user_message(&self) -> Option<&str> {
1106        match self {
1107            Self::Network(_) => Some("网络连接异常,请稍后重试"),
1108            Self::Authentication { .. } => Some("认证失败,请重新登录"),
1109            Self::Api(api) => Some(api.message.as_str()),
1110            Self::Validation { message, .. } => Some(message.as_str()),
1111            Self::Configuration { message, .. } => Some(message.as_str()),
1112            Self::Serialization { message, .. } => Some(message.as_str()),
1113            Self::Business { message, .. } => Some(message.as_str()),
1114            Self::Timeout { .. } => Some("请求超时,请稍后重试"),
1115            Self::RateLimit { .. } => Some("请求过于频繁,请稍候"),
1116            Self::ServiceUnavailable { .. } => Some("服务暂不可用,请稍后重试"),
1117            Self::Internal { message, .. } => Some(message.as_str()),
1118        }
1119    }
1120
1121    fn context(&self) -> &ErrorContext {
1122        self.ctx()
1123    }
1124
1125    fn error_type(&self) -> ErrorType {
1126        match self {
1127            Self::Network(_) => ErrorType::Network,
1128            Self::Authentication { .. } => ErrorType::Authentication,
1129            Self::Api(_) => ErrorType::Api,
1130            Self::Validation { .. } => ErrorType::Validation,
1131            Self::Configuration { .. } => ErrorType::Configuration,
1132            Self::Serialization { .. } => ErrorType::Serialization,
1133            Self::Business { .. } => ErrorType::Business,
1134            Self::Timeout { .. } => ErrorType::Timeout,
1135            Self::RateLimit { .. } => ErrorType::RateLimit,
1136            Self::ServiceUnavailable { .. } => ErrorType::ServiceUnavailable,
1137            Self::Internal { .. } => ErrorType::Internal,
1138        }
1139    }
1140
1141    fn error_code(&self) -> Option<&str> {
1142        None
1143    }
1144}
1145
1146// ============================================================================
1147// 便利函数(保持向后兼容)
1148// ============================================================================
1149
1150/// 创建网络错误
1151pub fn network_error(message: impl Into<String>) -> CoreError {
1152    CoreError::Network(Box::new(NetworkError {
1153        message: message.into(),
1154        source: None,
1155        policy: RetryPolicy::default(),
1156        ctx: Box::new(ErrorContext::new()),
1157    }))
1158}
1159
1160/// 创建认证错误
1161pub fn authentication_error(message: impl Into<String>) -> CoreError {
1162    CoreError::Authentication {
1163        message: message.into(),
1164        code: ErrorCode::AuthenticationFailed,
1165        ctx: Box::new(ErrorContext::new()),
1166    }
1167}
1168
1169/// 创建API错误
1170pub fn api_error(
1171    status: u16,
1172    endpoint: impl Into<String>,
1173    message: impl Into<String>,
1174    request_id: Option<String>,
1175) -> CoreError {
1176    CoreError::Api(Box::new(ApiError {
1177        status,
1178        endpoint: endpoint.into().into(),
1179        message: message.into(),
1180        source: None,
1181        code: ErrorCode::from_http_status(status),
1182        ctx: {
1183            let mut ctx = ErrorContext::new();
1184            if let Some(req_id) = request_id {
1185                ctx.set_request_id(req_id);
1186            }
1187            Box::new(ctx)
1188        },
1189    }))
1190}
1191
1192/// 创建验证错误
1193pub fn validation_error(field: impl Into<String>, message: impl Into<String>) -> CoreError {
1194    let field = field.into();
1195    let mut ctx = ErrorContext::new();
1196    ctx.add_context("field", field.clone());
1197
1198    CoreError::Validation {
1199        field: field.into(),
1200        message: message.into(),
1201        code: ErrorCode::ValidationError,
1202        ctx: Box::new(ctx),
1203    }
1204}
1205
1206/// 创建序列化错误
1207pub fn serialization_error<T: std::error::Error + Send + Sync + 'static>(
1208    message: impl Into<String>,
1209    source: Option<T>,
1210) -> CoreError {
1211    CoreError::Serialization {
1212        message: message.into(),
1213        source: source.map(|e| Box::new(e) as AnyError),
1214        code: ErrorCode::SerializationError,
1215        ctx: Box::new(ErrorContext::new()),
1216    }
1217}
1218
1219/// 创建业务错误
1220pub fn business_error(message: impl Into<String>) -> CoreError {
1221    CoreError::Business {
1222        message: message.into(),
1223        code: ErrorCode::BusinessError,
1224        ctx: Box::new(ErrorContext::new()),
1225    }
1226}
1227
1228/// 创建配置错误
1229pub fn configuration_error(message: impl Into<String>) -> CoreError {
1230    CoreError::Configuration {
1231        message: message.into(),
1232        code: ErrorCode::ConfigurationError,
1233        ctx: Box::new(ErrorContext::new()),
1234    }
1235}
1236
1237/// 创建超时错误
1238pub fn timeout_error(timeout: Duration, operation: Option<String>) -> CoreError {
1239    CoreError::Timeout {
1240        duration: timeout,
1241        operation,
1242        ctx: Box::new(ErrorContext::new()),
1243    }
1244}
1245
1246/// 创建限流错误
1247pub fn rate_limit_error(limit: u32, window: Duration, retry_after: Option<Duration>) -> CoreError {
1248    CoreError::RateLimit {
1249        limit,
1250        window,
1251        reset_after: retry_after,
1252        code: ErrorCode::TooManyRequests,
1253        ctx: Box::new(ErrorContext::new()),
1254    }
1255}
1256
1257/// 创建服务不可用错误
1258pub fn service_unavailable_error(
1259    service: impl Into<String>,
1260    retry_after: Option<Duration>,
1261) -> CoreError {
1262    CoreError::ServiceUnavailable {
1263        service: service.into().into(),
1264        retry_after,
1265        code: ErrorCode::ServiceUnavailable,
1266        ctx: Box::new(ErrorContext::new()),
1267    }
1268}
1269
1270/// 创建权限缺失错误
1271pub fn permission_missing_error(scopes: &[impl AsRef<str>]) -> CoreError {
1272    let mut ctx = ErrorContext::new();
1273    ctx.add_context(
1274        "required_scopes",
1275        scopes
1276            .iter()
1277            .map(|s| s.as_ref())
1278            .collect::<Vec<_>>()
1279            .join(","),
1280    );
1281
1282    CoreError::Authentication {
1283        message: "权限范围不足".to_string(),
1284        code: ErrorCode::PermissionMissing,
1285        ctx: Box::new(ctx),
1286    }
1287}
1288
1289/// 创建SSO令牌无效错误
1290pub fn sso_token_invalid_error(detail: impl Into<String>) -> CoreError {
1291    let mut ctx = ErrorContext::new();
1292    ctx.add_context("detail", detail.into());
1293
1294    CoreError::Authentication {
1295        message: "SSO令牌无效".to_string(),
1296        code: ErrorCode::SsoTokenInvalid,
1297        ctx: Box::new(ctx),
1298    }
1299}
1300
1301/// 创建用户身份无效错误
1302pub fn user_identity_invalid_error(desc: impl Into<String>) -> CoreError {
1303    let mut ctx = ErrorContext::new();
1304    ctx.add_context("description", desc.into());
1305
1306    CoreError::Authentication {
1307        message: "用户身份无效".to_string(),
1308        code: ErrorCode::UserIdentityInvalid,
1309        ctx: Box::new(ctx),
1310    }
1311}
1312
1313/// 创建访问令牌无效错误
1314pub fn token_invalid_error(detail: impl Into<String>) -> CoreError {
1315    let mut ctx = ErrorContext::new();
1316    ctx.add_context("detail", detail.into());
1317
1318    CoreError::Authentication {
1319        message: "访问令牌无效".to_string(),
1320        code: ErrorCode::AccessTokenInvalid,
1321        ctx: Box::new(ctx),
1322    }
1323}
1324
1325/// 创建访问令牌过期错误
1326pub fn token_expired_error(detail: impl Into<String>) -> CoreError {
1327    let mut ctx = ErrorContext::new();
1328    ctx.add_context("detail", detail.into());
1329
1330    CoreError::Authentication {
1331        message: "访问令牌过期".to_string(),
1332        code: ErrorCode::AccessTokenExpiredV2,
1333        ctx: Box::new(ctx),
1334    }
1335}
1336
1337/// 创建带详细信息的网络错误
1338pub fn network_error_with_details(
1339    message: impl Into<String>,
1340    endpoint: Option<String>,
1341    request_id: Option<String>,
1342) -> CoreError {
1343    let mut ctx = ErrorContext::new();
1344    if let Some(ep) = endpoint {
1345        ctx.add_context("endpoint", ep);
1346    }
1347    if let Some(req_id) = request_id {
1348        ctx.set_request_id(req_id);
1349    }
1350
1351    CoreError::Network(Box::new(NetworkError {
1352        message: message.into(),
1353        source: None,
1354        policy: RetryPolicy::default(),
1355        ctx: Box::new(ctx),
1356    }))
1357}
1358
1359#[cfg(test)]
1360mod tests {
1361    use super::*;
1362    use std::time::Duration;
1363
1364    #[test]
1365    fn api_error_has_code_and_severity() {
1366        let err = CoreError::api(503, "/ping", "service down", ErrorContext::new());
1367
1368        assert_eq!(err.code(), ErrorCode::ServiceUnavailable);
1369        assert!(err.is_retryable());
1370        assert_eq!(err.severity(), ErrorSeverity::Critical);
1371        assert!(err.retry_delay(1).is_some());
1372    }
1373
1374    #[test]
1375    fn record_contains_context() {
1376        let mut ctx = ErrorContext::new();
1377        ctx.add_context("endpoint", "/user");
1378        ctx.set_request_id("req-1");
1379
1380        let err = CoreError::network(std::io::Error::other("boom"), ctx);
1381
1382        let rec = err.record();
1383        assert_eq!(rec.code, ErrorCode::NetworkConnectionFailed);
1384        assert_eq!(rec.context.get("endpoint"), Some(&"/user".to_string()));
1385        assert_eq!(rec.request_id.as_deref(), Some("req-1"));
1386    }
1387
1388    #[test]
1389    fn core_error_to_record() {
1390        let err = CoreError::api(503, "/ping", "svc down", ErrorContext::new());
1391        let rec: ErrorRecord = (&err).into();
1392        assert_eq!(rec.code, ErrorCode::ServiceUnavailable);
1393        assert!(rec.retryable);
1394        assert!(rec.message.contains("API错误"));
1395    }
1396
1397    #[test]
1398    fn builder_creates_api_error_with_context() {
1399        let err = CoreError::api_builder()
1400            .status(404)
1401            .endpoint("/users/1")
1402            .message("not found")
1403            .request_id("req-123")
1404            .build();
1405
1406        assert!(err.is_api_error());
1407        assert_eq!(err.context().request_id(), Some("req-123"));
1408        assert_eq!(err.code(), ErrorCode::NotFound);
1409    }
1410
1411    #[test]
1412    fn rate_limit_retry_delay() {
1413        let err = CoreError::RateLimit {
1414            limit: 10,
1415            window: Duration::from_secs(60),
1416            reset_after: Some(Duration::from_secs(30)),
1417            code: ErrorCode::RateLimitExceeded,
1418            ctx: Box::new(ErrorContext::new()),
1419        };
1420
1421        assert!(err.is_retryable());
1422        assert_eq!(err.retry_delay(0), Some(Duration::from_secs(60)));
1423    }
1424
1425    #[test]
1426    fn from_reqwest_error() {
1427        // 无法直接构造 reqwest::Error(构造函数为私有),跳过具体实例化,只验证 From trait 存在
1428        fn assert_from_reqwest<E: Into<CoreError>>() {}
1429        assert_from_reqwest::<reqwest::Error>();
1430    }
1431
1432    #[test]
1433    fn map_context_covers_all_variants() {
1434        let errors = vec![
1435            network_error("n"),
1436            authentication_error("a"),
1437            api_error(500, "/api", "api", None),
1438            validation_error("field", "invalid"),
1439            configuration_error("cfg"),
1440            serialization_error("serde", None::<serde_json::Error>),
1441            business_error("biz"),
1442            timeout_error(Duration::from_secs(1), None),
1443            rate_limit_error(100, Duration::from_secs(60), Some(Duration::from_secs(10))),
1444            service_unavailable_error("svc", Some(Duration::from_secs(30))),
1445            CoreError::Internal {
1446                code: ErrorCode::InternalError,
1447                message: "internal".to_string(),
1448                source: None,
1449                ctx: Box::new(ErrorContext::new()),
1450            },
1451        ];
1452
1453        for err in errors {
1454            let updated = err.map_context(|ctx| {
1455                ctx.add_context("k", "v");
1456            });
1457            assert_eq!(updated.context().get_context("k"), Some("v"));
1458        }
1459    }
1460
1461    #[test]
1462    fn with_context_kv_adds_context() {
1463        let err = validation_error("field", "invalid").with_context_kv("user_id", "u-1");
1464        assert_eq!(err.context().get_context("user_id"), Some("u-1"));
1465    }
1466
1467    #[test]
1468    fn with_operation_updates_timeout_field_and_context() {
1469        let err = timeout_error(Duration::from_secs(30), Some("old_op".to_string()))
1470            .with_operation("new_op", "client");
1471
1472        match err {
1473            CoreError::Timeout {
1474                operation, ref ctx, ..
1475            } => {
1476                assert_eq!(operation.as_deref(), Some("new_op"));
1477                assert_eq!(ctx.operation(), Some("new_op"));
1478                assert_eq!(ctx.component(), Some("client"));
1479                assert_eq!(ctx.get_context("operation"), Some("new_op"));
1480                assert_eq!(ctx.get_context("component"), Some("client"));
1481            }
1482            other => panic!("expected timeout error, got {:?}", other.error_type()),
1483        }
1484    }
1485}