liurenjie1024 commented on code in PR #13:
URL: https://github.com/apache/iceberg-rust/pull/13#discussion_r1277391519
##########
src/error.rs:
##########
@@ -15,14 +15,319 @@
// specific language governing permissions and limitations
// under the License.
-use thiserror::Error;
-
-#[derive(Error, Debug)]
-pub enum IcebergError {
- #[error("The type `{0}` cannot be stored as bytes.")]
- ValueByteConversion(String),
- #[error("Failed to convert slice to array")]
- TryFromSlice(#[from] std::array::TryFromSliceError),
- #[error("Failed to convert u8 to string")]
- Utf8(#[from] std::str::Utf8Error),
+use std::backtrace::{Backtrace, BacktraceStatus};
+use std::fmt;
+use std::fmt::Debug;
+use std::fmt::Display;
+use std::fmt::Formatter;
+
+/// Result that is a wrapper of `Result<T, iceberg::Error>`
+pub type Result<T> = std::result::Result<T, Error>;
+
+/// ErrorKind is all kinds of Error of iceberg.
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[non_exhaustive]
+pub enum ErrorKind {
+ /// Iceberg don't know what happened here, and no actions other than
+ /// just returning it back. For example, iceberg returns an internal
+ /// service error.
+ Unexpected,
+
+ /// Iceberg data is invalid.
+ ///
+ /// This error is returned when we try to read a table from iceberg but
+ /// failed to parse it's metadata or data file correctly.
+ ///
+ /// The table could be invalid or corrupted.
+ DataInvalid,
+ /// Iceberg feature is not supported.
+ ///
+ /// This error is returned when given iceberg feature is not supported.
+ FeatureUnsupported,
+}
+
+impl ErrorKind {
+ /// Convert self into static str.
+ pub fn into_static(self) -> &'static str {
+ self.into()
+ }
+}
+
+impl From<ErrorKind> for &'static str {
+ fn from(v: ErrorKind) -> &'static str {
+ match v {
+ ErrorKind::Unexpected => "Unexpected",
+ ErrorKind::DataInvalid => "DataInvalid",
+ ErrorKind::FeatureUnsupported => "FeatureUnsupported",
+ }
+ }
+}
+
+impl Display for ErrorKind {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.into_static())
+ }
+}
+
+/// Error is the error struct returned by all iceberg functions.
+///
+/// ## Display
+///
+/// Error can be displayed in two ways:
+///
+/// - Via `Display`: like `err.to_string()` or `format!("{err}")`
+///
+/// Error will be printed in a single line:
+///
+/// ```shell
+/// Unexpected, context: { path: /path/to/file, called: send_async } =>
something wrong happened, source: networking error"
+/// ```
+///
+/// - Via `Debug`: like `format!("{err:?}")`
+///
+/// Error will be printed in multi lines with more details and backtraces (if
captured):
+///
+/// ```shell
+/// Unexpected => something wrong happened
+///
+/// Context:
+/// path: /path/to/file
+/// called: send_async
+///
+/// Source: networking error
+///
+/// Backtrace:
+/// 0: iceberg::error::Error::new
+/// at ./src/error.rs:197:24
+/// 1: iceberg::error::tests::generate_error
+/// at ./src/error.rs:241:9
+/// 2: iceberg::error::tests::test_error_debug_with_backtrace::{{closure}}
+/// at ./src/error.rs:305:41
+/// ...
+/// ```
+pub struct Error {
+ kind: ErrorKind,
+ message: String,
+
+ context: Vec<(&'static str, String)>,
+
+ source: Option<anyhow::Error>,
+ backtrace: Backtrace,
+}
+
+impl Display for Error {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.kind)?;
+
+ if !self.context.is_empty() {
+ write!(f, ", context: {{ ")?;
+ write!(
+ f,
+ "{}",
+ self.context
+ .iter()
+ .map(|(k, v)| format!("{k}: {v}"))
+ .collect::<Vec<_>>()
+ .join(", ")
+ )?;
+ write!(f, " }}")?;
+ }
+
+ if !self.message.is_empty() {
+ write!(f, " => {}", self.message)?;
+ }
+
+ if let Some(source) = &self.source {
+ write!(f, ", source: {source}")?;
+ }
+
+ Ok(())
+ }
+}
+
+impl Debug for Error {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ // If alternate has been specified, we will print like Debug.
+ if f.alternate() {
+ let mut de = f.debug_struct("Error");
+ de.field("kind", &self.kind);
+ de.field("message", &self.message);
+ de.field("context", &self.context);
+ de.field("source", &self.source);
+ de.field("backtrace", &self.backtrace);
+ return de.finish();
+ }
+
+ write!(f, "{}", self.kind)?;
+ if !self.message.is_empty() {
+ write!(f, " => {}", self.message)?;
+ }
+ writeln!(f)?;
+
+ if !self.context.is_empty() {
+ writeln!(f)?;
+ writeln!(f, "Context:")?;
+ for (k, v) in self.context.iter() {
+ writeln!(f, " {k}: {v}")?;
+ }
+ }
+ if let Some(source) = &self.source {
+ writeln!(f)?;
+ writeln!(f, "Source: {source:#}")?;
+ }
+
+ if self.backtrace.status() == BacktraceStatus::Captured {
+ writeln!(f)?;
+ writeln!(f, "Backtrace:")?;
+ writeln!(f, "{}", self.backtrace)?;
+ }
+
+ Ok(())
+ }
+}
+
+impl std::error::Error for Error {
+ fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
+ self.source.as_ref().map(|v| v.as_ref())
+ }
+}
+
+impl Error {
+ /// Create a new Error with error kind and message.
+ pub fn new(kind: ErrorKind, message: impl Into<String>) -> Self {
+ Self {
+ kind,
+ message: message.into(),
+ context: Vec::default(),
+
+ source: None,
+ // `Backtrace::capture()` will check if backtrace has been enabled
+ // internally. It's zero cost if backtrace is disabled.
+ backtrace: Backtrace::capture(),
+ }
+ }
+
+ /// Add more context in error.
+ pub fn with_context(mut self, key: &'static str, value: impl Into<String>)
-> Self {
+ self.context.push((key, value.into()));
+ self
+ }
+
+ /// Set source for error.
+ ///
+ /// # Notes
+ ///
+ /// If the source has been set, we will raise a panic here.
+ pub fn with_source(mut self, src: impl Into<anyhow::Error>) -> Self {
+ debug_assert!(self.source.is_none(), "the source error has been set");
+
+ self.source = Some(src.into());
+ self
+ }
+
+ /// Set the backtrace for error.
+ ///
+ /// This function is served as testing purpose and not intended to be
called
+ /// by users.
+ #[cfg(test)]
+ fn with_backtrace(mut self, backtrace: Backtrace) -> Self {
+ self.backtrace = backtrace;
+ self
+ }
+
+ /// Return error's kind.
+ ///
+ /// Users can use this method to check error's kind and take actions.
+ pub fn kind(&self) -> ErrorKind {
+ self.kind
+ }
+}
+
+impl From<std::str::Utf8Error> for Error {
Review Comment:
When I'm implementing icelake, this is not quite convenient. Maybe we should
implement a macro to reduce duplicates like this. I'll try it later.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]