rocketmq/
error.rs

1/*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements.  See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18//! Error data model of RocketMQ rust client.
19
20use std::error::Error;
21use std::fmt;
22use std::fmt::{Debug, Display, Formatter};
23
24/// Error type using by [`ClientError`].
25#[derive(thiserror::Error, Debug, PartialEq, Eq)]
26pub enum ErrorKind {
27    #[error("Failed to parse config")]
28    Config,
29
30    #[error("Failed to create session")]
31    Connect,
32
33    #[error("Message is invalid")]
34    InvalidMessage,
35
36    #[error("Server error")]
37    Server,
38
39    #[error("No broker available to send message")]
40    NoBrokerAvailable,
41
42    #[error("Client internal error")]
43    ClientInternal,
44
45    #[error("Client is not running")]
46    ClientIsNotRunning,
47
48    #[error("Failed to send message via channel")]
49    ChannelSend,
50
51    #[error("Failed to receive message via channel")]
52    ChannelReceive,
53
54    #[error("Unknown error")]
55    Unknown,
56}
57
58/// Error returned by producer or consumer.
59pub struct ClientError {
60    pub(crate) kind: ErrorKind,
61    pub(crate) message: String,
62    pub(crate) operation: &'static str,
63    pub(crate) context: Vec<(&'static str, String)>,
64    pub(crate) source: Option<anyhow::Error>,
65}
66
67impl Error for ClientError {}
68
69impl ClientError {
70    pub(crate) fn new(kind: ErrorKind, message: &str, operation: &'static str) -> Self {
71        Self {
72            kind,
73            message: message.to_string(),
74            operation,
75            context: Vec::new(),
76            source: None,
77        }
78    }
79
80    /// Error type
81    pub fn kind(&self) -> &ErrorKind {
82        &self.kind
83    }
84
85    /// Error message
86    pub fn message(&self) -> &str {
87        &self.message
88    }
89
90    /// Name of operation that produced this error
91    pub fn operation(&self) -> &str {
92        self.operation
93    }
94
95    /// Error context, formatted in key-value pairs
96    pub fn context(&self) -> &Vec<(&'static str, String)> {
97        &self.context
98    }
99
100    /// Source error
101    pub fn source(&self) -> Option<&anyhow::Error> {
102        self.source.as_ref()
103    }
104
105    pub(crate) fn with_operation(mut self, operation: &'static str) -> Self {
106        if !self.operation.is_empty() {
107            self.context.push(("called", self.operation.to_string()));
108        }
109
110        self.operation = operation;
111        self
112    }
113
114    pub(crate) fn with_context(mut self, key: &'static str, value: impl Into<String>) -> Self {
115        self.context.push((key, value.into()));
116        self
117    }
118
119    pub(crate) fn set_source(mut self, src: impl Into<anyhow::Error>) -> Self {
120        debug_assert!(self.source.is_none(), "the source error has been set");
121
122        self.source = Some(src.into());
123        self
124    }
125}
126
127impl Display for ClientError {
128    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
129        write!(f, "{} at {}", self.kind, self.operation)?;
130
131        if !self.context.is_empty() {
132            write!(f, ", context: {{ ")?;
133            write!(
134                f,
135                "{}",
136                self.context
137                    .iter()
138                    .map(|(k, v)| format!("{k}: {v}"))
139                    .collect::<Vec<_>>()
140                    .join(", ")
141            )?;
142            write!(f, " }}")?;
143        }
144
145        if !self.message.is_empty() {
146            write!(f, " => {}", self.message)?;
147        }
148
149        if let Some(source) = &self.source {
150            write!(f, ", source: {source}")?;
151        }
152
153        Ok(())
154    }
155}
156
157impl Debug for ClientError {
158    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
159        // If alternate has been specified, we will print like Debug.
160        if f.alternate() {
161            let mut debug = f.debug_struct("Error");
162            debug.field("kind", &self.kind);
163            debug.field("message", &self.message);
164            debug.field("operation", &self.operation);
165            debug.field("context", &self.context);
166            debug.field("source", &self.source);
167            return debug.finish();
168        }
169
170        write!(f, "{} at {}", self.kind, self.operation)?;
171        if !self.message.is_empty() {
172            write!(f, " => {}", self.message)?;
173        }
174        writeln!(f)?;
175
176        if !self.context.is_empty() {
177            writeln!(f)?;
178            writeln!(f, "Context:")?;
179            for (k, v) in self.context.iter() {
180                writeln!(f, "    {k}: {v}")?;
181            }
182        }
183        if let Some(source) = &self.source {
184            writeln!(f)?;
185            writeln!(f, "Source: {source:?}")?;
186        }
187
188        Ok(())
189    }
190}
191
192#[cfg(test)]
193mod tests {
194    use super::*;
195
196    #[test]
197    fn error_client_error() {
198        let err = ClientError::new(ErrorKind::Config, "fake_message", "error_client_error")
199            .with_operation("another_operation")
200            .with_context("context_key", "context_value")
201            .set_source(anyhow::anyhow!("fake_source_error"));
202        assert_eq!(
203            err.to_string(),
204            "Failed to parse config at another_operation, context: { called: error_client_error, context_key: context_value } => fake_message, source: fake_source_error"
205        );
206        assert_eq!(format!("{:?}", err), "Failed to parse config at another_operation => fake_message\n\nContext:\n    called: error_client_error\n    context_key: context_value\n\nSource: fake_source_error\n");
207        assert_eq!(format!("{:#?}", err), "Error {\n    kind: Config,\n    message: \"fake_message\",\n    operation: \"another_operation\",\n    context: [\n        (\n            \"called\",\n            \"error_client_error\",\n        ),\n        (\n            \"context_key\",\n            \"context_value\",\n        ),\n    ],\n    source: Some(\n        \"fake_source_error\",\n    ),\n}");
208    }
209}