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}