Move 17 library crates into crates/, CLI binary into bin/fxt, consolidate 10 test crates into testing/, split config crate from deployment config files. Root directory reduced from 38+ to ~17 directories. All Cargo.toml paths and build.rs proto refs updated. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
202 lines
6.5 KiB
Rust
202 lines
6.5 KiB
Rust
//! Macros for reducing service error boilerplate
|
|
//!
|
|
//! This module provides convenience macros to generate common error handling code
|
|
//! and reduce repetition across service-specific error implementations.
|
|
|
|
/// Generate a service error enum with CommonError wrapper and convenience constructors
|
|
///
|
|
/// This macro generates:
|
|
/// - An enum with a `Common(CommonError)` variant plus custom variants
|
|
/// - Constructor methods for each variant
|
|
/// - Automatic `From<CommonError>` implementation
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use common::error::define_service_error;
|
|
///
|
|
/// define_service_error! {
|
|
/// MyServiceError {
|
|
/// /// Custom error variant
|
|
/// CustomError { field: String, value: i32 },
|
|
///
|
|
/// /// Another custom variant
|
|
/// AnotherError(String),
|
|
/// }
|
|
/// }
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! define_service_error {
|
|
(
|
|
$(#[$meta:meta])*
|
|
$name:ident {
|
|
$(
|
|
$(#[$variant_meta:meta])*
|
|
$variant:ident $({ $($field:ident : $field_ty:ty),* $(,)? })?
|
|
$( ( $($tuple_ty:ty),* $(,)? ) )?
|
|
),* $(,)?
|
|
}
|
|
) => {
|
|
$(#[$meta])*
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub enum $name {
|
|
/// Common error with context
|
|
#[error("Service error: {0}")]
|
|
Common(#[from] $crate::error::CommonError),
|
|
|
|
$(
|
|
$(#[$variant_meta])*
|
|
#[error("{}", stringify!($variant))]
|
|
$variant $({ $($field: $field_ty,)* })? $( ( $($tuple_ty,)* ) )?,
|
|
)*
|
|
}
|
|
|
|
impl From<$name> for $crate::error::CommonError {
|
|
fn from(err: $name) -> Self {
|
|
use $crate::error::ServiceErrorExt;
|
|
err.to_common_error()
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Implement common convenience constructors for a service error
|
|
///
|
|
/// Generates constructors for common error types using `CommonError` builders.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use common::error::impl_common_error_constructors;
|
|
///
|
|
/// impl_common_error_constructors!(MyServiceError);
|
|
///
|
|
/// // Now you can use:
|
|
/// // MyServiceError::network("connection failed")
|
|
/// // MyServiceError::configuration("missing key")
|
|
/// // MyServiceError::validation("field", "invalid value")
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! impl_common_error_constructors {
|
|
($error_type:ty) => {
|
|
impl $error_type {
|
|
/// Create network error using CommonError
|
|
pub fn network<M: Into<String>>(message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::network(message))
|
|
}
|
|
|
|
/// Create configuration error using CommonError
|
|
pub fn configuration<M: Into<String>>(message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::config(message))
|
|
}
|
|
|
|
/// Create validation error using CommonError
|
|
pub fn validation<F: Into<String>, M: Into<String>>(field: F, message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::validation(format!(
|
|
"{}: {}",
|
|
field.into(),
|
|
message.into()
|
|
)))
|
|
}
|
|
|
|
/// Create timeout error using CommonError
|
|
pub fn timeout(actual_ms: u64, max_ms: u64) -> Self {
|
|
Self::Common($crate::error::CommonError::timeout(actual_ms, max_ms))
|
|
}
|
|
|
|
/// Create resource exhausted error using CommonError
|
|
pub fn resource_exhausted<R: Into<String>>(resource: R) -> Self {
|
|
Self::Common($crate::error::CommonError::resource_exhausted(resource))
|
|
}
|
|
|
|
/// Create internal error using CommonError
|
|
pub fn internal<M: Into<String>>(message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::internal(message))
|
|
}
|
|
|
|
/// Create authentication error using CommonError
|
|
pub fn authentication<M: Into<String>>(message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::service(
|
|
$crate::error::ErrorCategory::Authentication,
|
|
message.into(),
|
|
))
|
|
}
|
|
|
|
/// Create serialization error using CommonError
|
|
pub fn serialization<M: Into<String>>(message: M) -> Self {
|
|
Self::Common($crate::error::CommonError::serialization(message))
|
|
}
|
|
|
|
/// Create not found error using CommonError
|
|
pub fn not_found<R: Into<String>, I: Into<String>>(resource: R, identifier: I) -> Self {
|
|
Self::Common($crate::error::CommonError::service(
|
|
$crate::error::ErrorCategory::Resource,
|
|
format!("{} not found: {}", resource.into(), identifier.into()),
|
|
))
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// Implement standard error conversions for service errors
|
|
///
|
|
/// Generates `From` implementations for common error types.
|
|
///
|
|
/// # Example
|
|
///
|
|
/// ```rust
|
|
/// use common::error::impl_error_conversions;
|
|
///
|
|
/// impl_error_conversions!(MyServiceError, [std::io::Error, serde_json::Error]);
|
|
/// ```
|
|
#[macro_export]
|
|
macro_rules! impl_error_conversions {
|
|
($error_type:ty, [$($from_type:ty),* $(,)?]) => {
|
|
$(
|
|
impl From<$from_type> for $error_type {
|
|
fn from(err: $from_type) -> Self {
|
|
Self::Common($crate::error::CommonError::internal(format!("{}", err)))
|
|
}
|
|
}
|
|
)*
|
|
};
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use crate::error::{CommonError, ErrorCategory};
|
|
|
|
define_service_error! {
|
|
TestError {
|
|
CustomError { message: String },
|
|
TupleError(String, i32),
|
|
}
|
|
}
|
|
|
|
impl_common_error_constructors!(TestError);
|
|
|
|
#[test]
|
|
fn test_define_service_error() {
|
|
let _custom = TestError::CustomError {
|
|
message: "test".to_string(),
|
|
};
|
|
let _tuple = TestError::TupleError("test".to_string(), 42);
|
|
let _common = TestError::Common(CommonError::network("test"));
|
|
}
|
|
|
|
#[test]
|
|
fn test_common_error_constructors() {
|
|
let _network = TestError::network("connection failed");
|
|
let _config = TestError::configuration("missing key");
|
|
let _validation = TestError::validation("field", "invalid");
|
|
let _timeout = TestError::timeout(5000, 2000);
|
|
}
|
|
|
|
#[test]
|
|
fn test_error_conversion() {
|
|
let common_err = CommonError::network("test");
|
|
let service_err = TestError::Common(common_err);
|
|
let _back_to_common: CommonError = service_err.into();
|
|
}
|
|
}
|