use opentelemetry_otlp::ExporterBuildError;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum OtelError {
#[error("OTLP exporter build error: {0}")]
ExporterBuildError(#[from] ExporterBuildError),
#[error("unknown OTLP protocol: {0} (supported: 'grpc', 'http')")]
UnknownProtocol(String),
#[error("metrics shutdown error: {0}")]
MetricsShutdown(String),
#[error("configuration error: {0}")]
Configuration(String),
#[error("initialization error: {0}")]
Initialization(String),
#[error("tracing subscriber already initialized — only one can be set globally")]
SubscriberAlreadySet,
}
impl OtelError {
#[must_use]
pub fn config(msg: impl Into<String>) -> Self {
Self::Configuration(msg.into())
}
#[must_use]
pub fn init(msg: impl Into<String>) -> Self {
Self::Initialization(msg.into())
}
}
pub type OtelResult<T> = Result<T, OtelError>;
pub trait ErrorContext<T> {
fn context(self, ctx: impl Into<String>) -> OtelResult<T>;
fn with_context<F: FnOnce() -> String>(self, f: F) -> OtelResult<T>;
}
impl<T, E: std::error::Error + 'static> ErrorContext<T> for Result<T, E> {
fn context(self, ctx: impl Into<String>) -> OtelResult<T> {
self.map_err(|e| OtelError::Initialization(format!("{}: {e}", ctx.into())))
}
fn with_context<F: FnOnce() -> String>(self, f: F) -> OtelResult<T> {
self.map_err(|e| OtelError::Initialization(format!("{}: {e}", f())))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn error_display() {
let err = OtelError::UnknownProtocol("websocket".into());
assert!(err.to_string().contains("websocket"));
}
#[test]
fn config_error() {
let err = OtelError::config("bad endpoint");
assert!(err.to_string().contains("bad endpoint"));
}
}