From 8c66b98bca6ed0a2990903fe8e0ea72def5c7be8 Mon Sep 17 00:00:00 2001 From: HampusM Date: Sun, 21 Aug 2022 14:19:07 +0200 Subject: refactor!: change errors to be more sane BREAKING CHANGE: Major improvements have been made to error types and the error_stack crate is no longer used --- src/di_container.rs | 134 ++++++++++++++------------------------ src/di_container_binding_map.rs | 10 +-- src/errors/di_container.rs | 75 ++++++++++++++------- src/errors/injectable.rs | 41 ++++++++---- src/interfaces/any_factory.rs | 10 +++ src/interfaces/injectable.rs | 14 +++- src/libs/intertrait/cast/arc.rs | 15 ++--- src/libs/intertrait/cast/box.rs | 15 ++--- src/libs/intertrait/cast/error.rs | 21 ++---- src/libs/intertrait/cast/rc.rs | 15 ++--- src/libs/mod.rs | 1 - src/provider.rs | 11 ++-- 12 files changed, 183 insertions(+), 179 deletions(-) (limited to 'src') diff --git a/src/di_container.rs b/src/di_container.rs index 84fb9e0..c698fac 100644 --- a/src/di_container.rs +++ b/src/di_container.rs @@ -50,29 +50,15 @@ use std::any::type_name; use std::marker::PhantomData; -use error_stack::{report, Report, ResultExt}; - #[cfg(feature = "factory")] use crate::castable_factory::CastableFactory; use crate::di_container_binding_map::DIContainerBindingMap; use crate::errors::di_container::{BindingBuilderError, DIContainerError}; use crate::interfaces::injectable::Injectable; -use crate::libs::intertrait::cast::error::CastError; use crate::libs::intertrait::cast::{CastBox, CastRc}; use crate::provider::{Providable, SingletonProvider, TransientTypeProvider}; use crate::ptr::{SingletonPtr, TransientPtr}; -fn unable_to_cast_binding(err: Report) -> Report -where - Interface: 'static + ?Sized, -{ - err.change_context(DIContainerError) - .attach_printable(format!( - "Unable to cast binding for interface '{}'", - type_name::() - )) -} - /// Binding builder for type `Interface` inside a [`DIContainer`]. pub struct BindingBuilder<'di_container_lt, Interface> where @@ -100,7 +86,7 @@ where /// # Errors /// Will return Err if the associated [`DIContainer`] already have a binding for /// the interface. - pub fn to(&mut self) -> error_stack::Result<(), BindingBuilderError> + pub fn to(&mut self) -> Result<(), BindingBuilderError> where Implementation: Injectable, { @@ -108,10 +94,7 @@ where .bindings .set::(Box::new(TransientTypeProvider::::new())) .ok_or_else(|| { - report!(BindingBuilderError).attach_printable(format!( - "Binding already exists for interface '{}'", - type_name::() - )) + BindingBuilderError::BindingAlreadyExists(type_name::()) })?; Ok(()) @@ -123,25 +106,20 @@ where /// # Errors /// Will return Err if creating the singleton fails or if the /// associated [`DIContainer`] already have a binding for the interface. - pub fn to_singleton( - &mut self, - ) -> error_stack::Result<(), BindingBuilderError> + pub fn to_singleton(&mut self) -> Result<(), BindingBuilderError> where Implementation: Injectable, { let singleton: SingletonPtr = SingletonPtr::from( Implementation::resolve(self.di_container, Vec::new()) - .change_context(BindingBuilderError)?, + .map_err(BindingBuilderError::SingletonResolveFailed)?, ); self.di_container .bindings .set::(Box::new(SingletonProvider::new(singleton))) .ok_or_else(|| { - report!(BindingBuilderError).attach_printable(format!( - "Binding already exists for interface '{}'", - type_name::() - )) + BindingBuilderError::BindingAlreadyExists(type_name::()) })?; Ok(()) @@ -159,7 +137,7 @@ where pub fn to_factory( &mut self, factory_func: &'static dyn Fn>, - ) -> error_stack::Result<(), BindingBuilderError> + ) -> Result<(), BindingBuilderError> where Args: 'static, Return: 'static + ?Sized, @@ -173,10 +151,7 @@ where crate::ptr::FactoryPtr::new(factory_impl), ))) .ok_or_else(|| { - report!(BindingBuilderError).attach_printable(format!( - "Binding already exists for interface '{}'", - type_name::() - )) + BindingBuilderError::BindingAlreadyExists(type_name::()) })?; Ok(()) @@ -194,7 +169,7 @@ where pub fn to_default_factory( &mut self, factory_func: &'static dyn Fn<(), Output = TransientPtr>, - ) -> error_stack::Result<(), BindingBuilderError> + ) -> Result<(), BindingBuilderError> where Return: 'static + ?Sized, { @@ -206,10 +181,7 @@ where crate::ptr::FactoryPtr::new(factory_impl), ))) .ok_or_else(|| { - report!(BindingBuilderError).attach_printable(format!( - "Binding already exists for interface '{}'", - type_name::() - )) + BindingBuilderError::BindingAlreadyExists(type_name::()) })?; Ok(()) @@ -249,9 +221,7 @@ impl DIContainer /// - Resolving the binding for `Interface` fails /// - Casting the binding for `Interface` fails /// - The binding for `Interface` is not transient - pub fn get( - &self, - ) -> error_stack::Result, DIContainerError> + pub fn get(&self) -> Result, DIContainerError> where Interface: 'static + ?Sized, { @@ -268,7 +238,7 @@ impl DIContainer /// - The binding for `Interface` is not a singleton pub fn get_singleton( &self, - ) -> error_stack::Result, DIContainerError> + ) -> Result, DIContainerError> where Interface: 'static + ?Sized, { @@ -288,7 +258,7 @@ impl DIContainer #[cfg(feature = "factory")] pub fn get_factory( &self, - ) -> error_stack::Result, DIContainerError> + ) -> Result, DIContainerError> where Interface: 'static + ?Sized, { @@ -297,20 +267,21 @@ impl DIContainer if let Providable::Factory(binding_factory) = binding_providable { return binding_factory .cast::() - .map_err(unable_to_cast_binding::); + .map_err(|_| DIContainerError::CastFailed(type_name::())); } - Err(report!(DIContainerError).attach_printable(format!( - "Binding for interface '{}' is not a factory", - type_name::() - ))) + Err(DIContainerError::WrongBindingType { + interface: type_name::(), + expected: "factory", + found: binding_providable.to_string().to_lowercase(), + }) } #[doc(hidden)] pub fn get_with_history( &self, dependency_history: Vec<&'static str>, - ) -> error_stack::Result, DIContainerError> + ) -> Result, DIContainerError> where Interface: 'static + ?Sized, { @@ -320,7 +291,7 @@ impl DIContainer if let Providable::Transient(binding_transient) = binding_providable { return binding_transient .cast::() - .map_err(unable_to_cast_binding::); + .map_err(|_| DIContainerError::CastFailed(type_name::())); } #[cfg(feature = "factory")] @@ -329,22 +300,23 @@ impl DIContainer let factory = binding_factory .cast::>() - .map_err(unable_to_cast_binding::)?; + .map_err(|_| DIContainerError::CastFailed(type_name::()))?; return Ok(factory()); } - Err(report!(DIContainerError).attach_printable(format!( - "Binding for interface '{}' is not transient", - type_name::() - ))) + Err(DIContainerError::WrongBindingType { + interface: type_name::(), + expected: "transient", + found: binding_providable.to_string().to_lowercase(), + }) } #[doc(hidden)] pub fn get_singleton_with_history( &self, dependency_history: Vec<&'static str>, - ) -> error_stack::Result, DIContainerError> + ) -> Result, DIContainerError> where Interface: 'static + ?Sized, { @@ -354,30 +326,30 @@ impl DIContainer if let Providable::Singleton(binding_singleton) = binding_providable { return binding_singleton .cast::() - .map_err(unable_to_cast_binding::); + .map_err(|_| DIContainerError::CastFailed(type_name::())); } - Err(report!(DIContainerError).attach_printable(format!( - "Binding for interface '{}' is not a singleton", - type_name::() - ))) + Err(DIContainerError::WrongBindingType { + interface: type_name::(), + expected: "singleton", + found: binding_providable.to_string().to_lowercase(), + }) } fn get_binding_providable( &self, dependency_history: Vec<&'static str>, - ) -> error_stack::Result + ) -> Result where Interface: 'static + ?Sized, { self.bindings .get::()? .provide(self, dependency_history) - .change_context(DIContainerError) - .attach_printable(format!( - "Failed to resolve binding for interface '{}'", - type_name::() - )) + .map_err(|err| DIContainerError::BindingResolveFailed { + reason: err, + interface: type_name::(), + }) } } @@ -395,7 +367,7 @@ mod tests use mockall::mock; use super::*; - use crate::errors::injectable::ResolveError; + use crate::errors::injectable::InjectableError; use crate::provider::IProvider; use crate::ptr::TransientPtr; @@ -450,10 +422,7 @@ mod tests fn resolve( _di_container: &DIContainer, _dependency_history: Vec<&'static str>, - ) -> error_stack::Result< - TransientPtr, - crate::errors::injectable::ResolveError, - > + ) -> Result, crate::errors::injectable::InjectableError> where Self: Sized, { @@ -517,10 +486,7 @@ mod tests fn resolve( _di_container: &DIContainer, _dependency_history: Vec<&'static str>, - ) -> error_stack::Result< - TransientPtr, - crate::errors::injectable::ResolveError, - > + ) -> Result, crate::errors::injectable::InjectableError> where Self: Sized, { @@ -530,7 +496,7 @@ mod tests } #[test] - fn can_bind_to() -> error_stack::Result<(), BindingBuilderError> + fn can_bind_to() -> Result<(), BindingBuilderError> { let mut di_container: DIContainer = DIContainer::new(); @@ -546,7 +512,7 @@ mod tests } #[test] - fn can_bind_to_singleton() -> error_stack::Result<(), BindingBuilderError> + fn can_bind_to_singleton() -> Result<(), BindingBuilderError> { let mut di_container: DIContainer = DIContainer::new(); @@ -563,7 +529,7 @@ mod tests #[test] #[cfg(feature = "factory")] - fn can_bind_to_factory() -> error_stack::Result<(), BindingBuilderError> + fn can_bind_to_factory() -> Result<(), BindingBuilderError> { type IUserManagerFactory = dyn crate::interfaces::factory::IFactory<(), dyn subjects::IUserManager>; @@ -585,7 +551,7 @@ mod tests } #[test] - fn can_get() -> error_stack::Result<(), DIContainerError> + fn can_get() -> Result<(), DIContainerError> { mock! { Provider {} @@ -596,7 +562,7 @@ mod tests &self, di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result; + ) -> Result; } } @@ -620,7 +586,7 @@ mod tests } #[test] - fn can_get_singleton() -> error_stack::Result<(), DIContainerError> + fn can_get_singleton() -> Result<(), DIContainerError> { mock! { Provider {} @@ -631,7 +597,7 @@ mod tests &self, di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result; + ) -> Result; } } @@ -664,7 +630,7 @@ mod tests #[test] #[cfg(feature = "factory")] - fn can_get_factory() -> error_stack::Result<(), DIContainerError> + fn can_get_factory() -> Result<(), DIContainerError> { trait IUserManager { @@ -717,7 +683,7 @@ mod tests &self, di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result; + ) -> Result; } } diff --git a/src/di_container_binding_map.rs b/src/di_container_binding_map.rs index fee33b0..e64ff17 100644 --- a/src/di_container_binding_map.rs +++ b/src/di_container_binding_map.rs @@ -1,7 +1,6 @@ use std::any::{type_name, TypeId}; use ahash::AHashMap; -use error_stack::report; use crate::{errors::di_container::DIContainerError, provider::IProvider}; @@ -19,7 +18,7 @@ impl DIContainerBindingMap } } - pub fn get(&self) -> error_stack::Result<&dyn IProvider, DIContainerError> + pub fn get(&self) -> Result<&dyn IProvider, DIContainerError> where Interface: 'static + ?Sized, { @@ -28,12 +27,7 @@ impl DIContainerBindingMap Ok(self .bindings .get(&interface_typeid) - .ok_or_else(|| { - report!(DIContainerError).attach_printable(format!( - "No binding exists for interface '{}'", - type_name::() - )) - })? + .ok_or_else(|| DIContainerError::BindingNotFound(type_name::()))? .as_ref()) } diff --git a/src/errors/di_container.rs b/src/errors/di_container.rs index 127676f..ed05a5e 100644 --- a/src/errors/di_container.rs +++ b/src/errors/di_container.rs @@ -1,34 +1,61 @@ -//! Error types for the DI container. +//! Error types for [`DIContainer`] and it's related structs. +//! +//! [`DIContainer`]: crate::di_container::DIContainer -use std::fmt; -use std::fmt::{Display, Formatter}; +use crate::errors::injectable::InjectableError; -use error_stack::Context; - -/// Error for when the DI container fails to do something. -#[derive(Debug)] -pub struct DIContainerError; - -impl Display for DIContainerError +/// Error type for [`DIContainer`]. +/// +/// [`DIContainer`]: crate::di_container::DIContainer +#[derive(thiserror::Error, Debug)] +pub enum DIContainerError { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + /// Unable to cast a binding for a interface. + #[error("Unable to cast binding for interface '{0}'")] + CastFailed(&'static str), + + /// Wrong binding type. + #[error("Wrong binding type for interface '{interface}'. Expected a {expected}. Found a {found}")] + WrongBindingType { - fmt.write_str("A DI container error has occurred") - } -} + /// The affected bound interface. + interface: &'static str, -impl Context for DIContainerError {} + /// The expected binding type. + expected: &'static str, -/// Error for when the binding builder fails to do something. -#[derive(Debug)] -pub struct BindingBuilderError; + /// The found binding type. + found: String, + }, -impl Display for BindingBuilderError -{ - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + /// Failed to resolve a binding for a interface. + #[error("Failed to resolve binding for interface '{interface}'")] + BindingResolveFailed { - fmt.write_str("A binding builder error has occurred") - } + /// The reason for the problem. + #[source] + reason: InjectableError, + + /// The affected bound interface. + interface: &'static str, + }, + + /// No binding exists for a interface. + #[error("No binding exists for interface '{0}'")] + BindingNotFound(&'static str), } -impl Context for BindingBuilderError {} +/// Error type for [`BindingBuilder`]. +/// +/// [`BindingBuilder`]: crate::di_container::BindingBuilder +#[derive(thiserror::Error, Debug)] +pub enum BindingBuilderError +{ + /// A binding already exists for a interface. + #[error("Binding already exists for interface '{0}'")] + BindingAlreadyExists(&'static str), + + /// Resolving a singleton failed. + #[error("Resolving the given singleton failed")] + SingletonResolveFailed(#[from] InjectableError), +} diff --git a/src/errors/injectable.rs b/src/errors/injectable.rs index 63afa11..4b9af96 100644 --- a/src/errors/injectable.rs +++ b/src/errors/injectable.rs @@ -1,20 +1,33 @@ -//! Error types for structs implementing Injectable. +#![allow(clippy::module_name_repetitions)] +//! Error types for structs that implement [`Injectable`]. +//! +//! [`Injectable`]: crate::interfaces::injectable::Injectable -use core::fmt; -use std::fmt::{Display, Formatter}; +use super::di_container::DIContainerError; -use error_stack::Context; +/// Error type for structs that implement [`Injectable`]. +/// +/// [`Injectable`]: crate::interfaces::injectable::Injectable +#[derive(thiserror::Error, Debug)] +pub enum InjectableError +{ + /// Failed to resolve dependencies. + #[error("Failed to resolve a dependency of '{affected}'")] + ResolveFailed + { + /// The reason for the problem. + #[source] + reason: Box, -/// Error for when a injectable struct fails to be resolved. -#[derive(Debug)] -pub struct ResolveError; + /// The affected injectable type. + affected: &'static str, + }, -impl Display for ResolveError -{ - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + /// Detected circular dependencies. + #[error("Detected circular dependencies. {dependency_trace}")] + DetectedCircular { - fmt.write_str("Failed to resolve injectable struct") - } + /// A visual trace of dependencies. + dependency_trace: String, + }, } - -impl Context for ResolveError {} diff --git a/src/interfaces/any_factory.rs b/src/interfaces/any_factory.rs index 98ec144..887bb61 100644 --- a/src/interfaces/any_factory.rs +++ b/src/interfaces/any_factory.rs @@ -1,6 +1,16 @@ //! Interface for any factory to ever exist. +use std::fmt::Debug; + use crate::libs::intertrait::CastFrom; /// Interface for any factory to ever exist. pub trait AnyFactory: CastFrom {} + +impl Debug for dyn AnyFactory +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + f.write_str("{}") + } +} diff --git a/src/interfaces/injectable.rs b/src/interfaces/injectable.rs index e6e4ced..f90b79d 100644 --- a/src/interfaces/injectable.rs +++ b/src/interfaces/injectable.rs @@ -1,5 +1,7 @@ //! Interface for structs that can be injected into or be injected to. -use crate::errors::injectable::ResolveError; +use std::fmt::Debug; + +use crate::errors::injectable::InjectableError; use crate::libs::intertrait::CastFrom; use crate::ptr::TransientPtr; use crate::DIContainer; @@ -14,7 +16,15 @@ pub trait Injectable: CastFrom fn resolve( di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result, ResolveError> + ) -> Result, InjectableError> where Self: Sized; } + +impl Debug for dyn Injectable +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result + { + f.write_str("{}") + } +} diff --git a/src/libs/intertrait/cast/arc.rs b/src/libs/intertrait/cast/arc.rs index f8e0f11..65ae1ef 100644 --- a/src/libs/intertrait/cast/arc.rs +++ b/src/libs/intertrait/cast/arc.rs @@ -12,8 +12,6 @@ use std::any::type_name; use std::sync::Arc; -use error_stack::report; - use crate::libs::intertrait::cast::error::CastError; use crate::libs::intertrait::{caster, CastFromSync}; @@ -22,7 +20,7 @@ pub trait CastArc /// Casts an `Arc` for this trait into that for type `OtherTrait`. fn cast( self: Arc, - ) -> error_stack::Result, CastError>; + ) -> Result, CastError>; } /// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and `Send`. @@ -30,15 +28,14 @@ impl CastArc for CastFromSelf { fn cast( self: Arc, - ) -> error_stack::Result, CastError> + ) -> Result, CastError> { match caster::((*self).type_id()) { Some(caster) => Ok((caster.cast_arc)(self.arc_any())), - None => Err(report!(CastError).attach_printable(format!( - "From {} to {}", - type_name::(), - type_name::() - ))), + None => Err(CastError::CastFailed { + from: type_name::(), + to: type_name::(), + }), } } } diff --git a/src/libs/intertrait/cast/box.rs b/src/libs/intertrait/cast/box.rs index 11f631a..31f06db 100644 --- a/src/libs/intertrait/cast/box.rs +++ b/src/libs/intertrait/cast/box.rs @@ -12,8 +12,6 @@ use std::any::type_name; -use error_stack::report; - use crate::libs::intertrait::cast::error::CastError; use crate::libs::intertrait::{caster, CastFrom}; @@ -22,7 +20,7 @@ pub trait CastBox /// Casts a box to this trait into that of type `OtherTrait`. fn cast( self: Box, - ) -> error_stack::Result, CastError>; + ) -> Result, CastError>; } /// A blanket implementation of `CastBox` for traits extending `CastFrom`. @@ -30,15 +28,14 @@ impl CastBox for CastFromSelf { fn cast( self: Box, - ) -> error_stack::Result, CastError> + ) -> Result, CastError> { match caster::((*self).type_id()) { Some(caster) => Ok((caster.cast_box)(self.box_any())), - None => Err(report!(CastError).attach_printable(format!( - "From {} to {}", - type_name::(), - type_name::() - ))), + None => Err(CastError::CastFailed { + from: type_name::(), + to: type_name::(), + }), } } } diff --git a/src/libs/intertrait/cast/error.rs b/src/libs/intertrait/cast/error.rs index e4211b1..74eb3ca 100644 --- a/src/libs/intertrait/cast/error.rs +++ b/src/libs/intertrait/cast/error.rs @@ -1,17 +1,10 @@ -use std::fmt; -use std::fmt::{Display, Formatter}; - -use error_stack::Context; - -#[derive(Debug)] -pub struct CastError; - -impl Display for CastError +#[derive(thiserror::Error, Debug)] +pub enum CastError { - fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result + #[error("Failed to cast from trait {from} to trait {to}")] + CastFailed { - fmt.write_str("Failed to cast between traits") - } + from: &'static str, + to: &'static str, + }, } - -impl Context for CastError {} diff --git a/src/libs/intertrait/cast/rc.rs b/src/libs/intertrait/cast/rc.rs index 6cd377e..dfb71c2 100644 --- a/src/libs/intertrait/cast/rc.rs +++ b/src/libs/intertrait/cast/rc.rs @@ -12,8 +12,6 @@ use std::any::type_name; use std::rc::Rc; -use error_stack::report; - use crate::libs::intertrait::cast::error::CastError; use crate::libs::intertrait::{caster, CastFrom}; @@ -22,7 +20,7 @@ pub trait CastRc /// Casts an `Rc` for this trait into that for type `OtherTrait`. fn cast( self: Rc, - ) -> error_stack::Result, CastError>; + ) -> Result, CastError>; } /// A blanket implementation of `CastRc` for traits extending `CastFrom`. @@ -30,15 +28,14 @@ impl CastRc for CastFromSelf { fn cast( self: Rc, - ) -> error_stack::Result, CastError> + ) -> Result, CastError> { match caster::((*self).type_id()) { Some(caster) => Ok((caster.cast_rc)(self.rc_any())), - None => Err(report!(CastError).attach_printable(format!( - "From {} to {}", - type_name::(), - type_name::() - ))), + None => Err(CastError::CastFailed { + from: type_name::(), + to: type_name::(), + }), } } } diff --git a/src/libs/mod.rs b/src/libs/mod.rs index 034d11d..8d5583d 100644 --- a/src/libs/mod.rs +++ b/src/libs/mod.rs @@ -1,4 +1,3 @@ pub mod intertrait; -pub extern crate error_stack; pub extern crate linkme; diff --git a/src/provider.rs b/src/provider.rs index e12a12a..ad94589 100644 --- a/src/provider.rs +++ b/src/provider.rs @@ -1,12 +1,13 @@ #![allow(clippy::module_name_repetitions)] use std::marker::PhantomData; -use crate::errors::injectable::ResolveError; +use crate::errors::injectable::InjectableError; use crate::interfaces::any_factory::AnyFactory; use crate::interfaces::injectable::Injectable; use crate::ptr::{FactoryPtr, SingletonPtr, TransientPtr}; use crate::DIContainer; +#[derive(strum_macros::Display, Debug)] pub enum Providable { Transient(TransientPtr), @@ -21,7 +22,7 @@ pub trait IProvider &self, di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result; + ) -> Result; } pub struct TransientTypeProvider @@ -51,7 +52,7 @@ where &self, di_container: &DIContainer, dependency_history: Vec<&'static str>, - ) -> error_stack::Result + ) -> Result { Ok(Providable::Transient(InjectableType::resolve( di_container, @@ -85,7 +86,7 @@ where &self, _di_container: &DIContainer, _dependency_history: Vec<&'static str>, - ) -> error_stack::Result + ) -> Result { Ok(Providable::Singleton(self.singleton.clone())) } @@ -113,7 +114,7 @@ impl IProvider for FactoryProvider &self, _di_container: &DIContainer, _dependency_history: Vec<&'static str>, - ) -> error_stack::Result + ) -> Result { Ok(Providable::Factory(self.factory.clone())) } -- cgit v1.2.3-18-g5258