1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//! Utilities for decomposing error types.
use Error;
use crateRaftTypeConfig;
use crateInfallible;
use crateRPCError;
use crateRaftError;
use crateUnreachable;
use crateinto_ok;
/// Simplifies error handling by extracting the inner error from a composite error.
///
/// This trait helps handle nested error types common in Raft operations where
/// errors can be either recoverable API errors or fatal system errors.
///
/// It converts `Result<R, CompositeError>` to `Result<Result<R, Self::InnerError>, OuterError>`,
/// where `CompositeError` is a composite of `Self::InnerError` and `OuterError`.
///
/// # Implementations
///
/// - `Result<R, RaftError<C, E>>` decomposes into `Result<Result<R, E>, RaftError<C>>`, separating
/// API errors (`E`) from fatal errors.
///
/// - `Result<R, RPCError<C, RaftError<C, E>>>` decomposes into `Result<Result<R, E>, RPCError<C>>`,
/// separating API errors from transport and fatal errors. Note: `RaftError::Fatal` is converted
/// to `RPCError::Unreachable`.
///
/// # Example: Handling RaftError
///
/// ```ignore
/// use openraft::error::{DecomposeResult, RaftError, ClientWriteError};
///
/// async fn handle_write(raft: &Raft<Config>, request: Request) -> Result<(), AppError> {
/// let result = raft.client_write(request).await;
///
/// // Decompose separates Fatal errors from API errors
/// match result.decompose() {
/// Ok(Ok(response)) => {
/// // Success - process the response
/// Ok(())
/// }
/// Ok(Err(ClientWriteError::ForwardToLeader(fwd))) => {
/// // Recoverable: forward request to the current leader
/// Err(AppError::NotLeader(fwd.leader_id))
/// }
/// Ok(Err(ClientWriteError::ChangeMembershipError(_))) => {
/// // Recoverable: membership change in progress, retry later
/// Err(AppError::RetryLater)
/// }
/// Err(RaftError::Fatal(fatal)) => {
/// // Fatal: Raft node is shutting down or storage failed
/// Err(AppError::RaftStopped(fatal))
/// }
/// Err(RaftError::APIError(_)) => {
/// // This branch is unreachable after decompose()
/// unreachable!()
/// }
/// }
/// }
/// ```
///
/// # Example: Handling RPCError
///
/// ```ignore
/// use openraft::error::{DecomposeResult, RPCError};
///
/// async fn forward_to_leader<C: RaftTypeConfig>(
/// network: &mut Network,
/// request: Request
/// ) -> Result<Response, AppError> {
/// let result: Result<Response, RPCError<C, RaftError<C, ClientWriteError<C>>>>
/// = network.send_request(request).await;
///
/// match result.decompose() {
/// Ok(Ok(response)) => Ok(response),
/// Ok(Err(api_error)) => {
/// // Remote node returned an API error (e.g., ForwardToLeader)
/// Err(AppError::from(api_error))
/// }
/// Err(RPCError::Timeout(_)) => Err(AppError::Timeout),
/// Err(RPCError::Unreachable(_)) => Err(AppError::NodeDown),
/// Err(RPCError::Network(_)) => Err(AppError::NetworkError),
/// Err(RPCError::RemoteError(_)) => {
/// // This branch is unreachable after decompose()
/// unreachable!()
/// }
/// }
/// }
/// ```