timber_rust/service/error.rs
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2026 Dante Doménech Martinez dante19031999@gmail.com
3
4use crate::service::serror::FeatureDisabledError;
5#[cfg(feature = "network")]
6use crate::service::HttpError;
7use std::fmt::{Display, Formatter};
8use std::sync::{MutexGuard, PoisonError};
9
10/// Represents the possible failure modes for a [`Service`][`crate::Service`].
11///
12/// This enum wraps specific backend errors into a unified type, allowing
13/// the [`LoggerImpl`][`crate::LoggerImpl`] to handle various failure scenarios consistently.
14#[derive(Debug)]
15pub enum ServiceError {
16 /// Errors related to the file system or standard I/O operations.
17 ///
18 /// This is typically triggered by permission issues, missing files, or
19 /// interrupted streams during log rotation or local storage.
20 Io(std::io::Error),
21 /// Errors occurring during string formatting operations.
22 ///
23 /// Usually happens when writing to a buffer or formatting complex
24 /// log structures for output.
25 Fmt(std::fmt::Error),
26 /// Errors occurring during JSON serialization or deserialization.
27 ///
28 /// Triggered when the log message cannot be converted to valid JSON
29 /// or when a configuration file has an invalid JSON format.
30 #[cfg(feature = "json")]
31 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
32 Json(serde_json::Error),
33 /// Errors occurring during network requests (e.g., HTTP).
34 ///
35 /// This includes connection timeouts, DNS resolution failures, or
36 /// transport-level issues when sending logs to a remote server.
37 #[cfg(feature = "network")]
38 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
39 Network(reqwest::Error),
40 /// Errors returned by the HTTP server or protocol.
41 ///
42 /// Unlike [`ServiceError::Network`], this variant represents valid HTTP
43 /// responses that indicate a failure (e.g., 4xx or 5xx status codes).
44 #[cfg(feature = "network")]
45 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
46 Http(HttpError),
47 /// A synchronization primitive has been "poisoned".
48 ///
49 /// This occurs when a thread panics while holding a [`std::sync::Mutex`]
50 /// or [`std::sync::RwLock`]. The data protected by the lock might be
51 /// in an inconsistent state.
52 LockPoisoned,
53 /// Failed to map the raw configuration into the internal settings structure.
54 ///
55 /// This indicates that the configuration exists and is syntactically correct,
56 /// but contains invalid values or missing required fields.
57 ConfigDeserialization,
58 /// The requested operation requires a feature that is not currently enabled.
59 ///
60 /// Check the [`FeatureDisabledError`] for details on which feature is missing
61 /// and how to enable it in your `Cargo.toml`.
62 FeatureDisabled(FeatureDisabledError),
63 /// A catch-all for errors not covered by the specific variants.
64 ///
65 /// Use this for wrapping third-party errors that don't justify
66 /// a dedicated variant or for rare, unexpected conditions.
67 Unknown(Box<dyn std::error::Error + Send + Sync>),
68}
69impl Display for ServiceError {
70 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
71 match self {
72 Self::Io(err) => write!(f, "IO error: {}", err),
73 Self::Fmt(err) => write!(f, "Fmt error: {}", err),
74 #[cfg(feature = "json")]
75 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
76 Self::Json(err) => write!(f, "JSON error: {}", err),
77 #[cfg(feature = "network")]
78 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
79 Self::Network(err) => write!(f, "Network error: {}", err),
80 #[cfg(feature = "network")]
81 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
82 Self::Http(err) => write!(f, "Http error: {}", err.status_code()),
83 Self::LockPoisoned => write!(f, "Lock poisoned"),
84 Self::ConfigDeserialization => write!(f, "Config deserialization error"),
85 Self::FeatureDisabled(err) => write!(f, "{}", err),
86 Self::Unknown(err) => write!(f, "Unknown service error: {}", err),
87 }
88 }
89}
90
91impl std::error::Error for ServiceError {
92 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
93 match self {
94 Self::Io(err) => Some(err),
95 Self::Fmt(err) => Some(err),
96 #[cfg(feature = "json")]
97 #[cfg_attr(docsrs, doc(cfg(feature = "json")))]
98 Self::Json(err) => Some(err),
99 #[cfg(feature = "network")]
100 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
101 Self::Network(err) => Some(err),
102 #[cfg(feature = "network")]
103 #[cfg_attr(docsrs, doc(cfg(feature = "network")))]
104 Self::Http(err) => Some(err),
105 Self::LockPoisoned => None,
106 Self::ConfigDeserialization => None,
107 Self::FeatureDisabled(err) => Some(err),
108 Self::Unknown(err) => Some(err.as_ref()),
109 }
110 }
111}
112
113impl From<std::fmt::Error> for ServiceError {
114 fn from(err: std::fmt::Error) -> Self {
115 ServiceError::Fmt(err)
116 }
117}
118
119impl From<std::io::Error> for ServiceError {
120 fn from(err: std::io::Error) -> Self {
121 ServiceError::Io(err)
122 }
123}
124
125impl<T> From<PoisonError<MutexGuard<'_, T>>> for ServiceError {
126 fn from(_err: PoisonError<MutexGuard<'_, T>>) -> Self {
127 ServiceError::LockPoisoned
128 }
129}