1use std::any::Any;
2use thiserror::Error;
3
4#[derive(Error, Debug, Clone)]
9#[non_exhaustive]
10pub enum RdiError {
11 #[error("service not registered: {ty}{}", context.as_ref().map(|c| format!(" (while resolving {c})")).unwrap_or_default())]
12 ServiceNotFound {
13 ty: &'static str,
14 context: Option<String>,
15 },
16 #[error("keyed service not registered: key={key}, type={ty}{}", context.as_ref().map(|c| format!(" (while resolving {c})")).unwrap_or_default())]
17 KeyedServiceNotFound {
18 key: String,
19 ty: &'static str,
20 context: Option<String>,
21 },
22 #[error("circular dependency detected: {0}")]
23 CircularDependency(String),
24 #[error("singleton '{singleton}' cannot depend on scoped service '{scoped}'")]
25 SingletonDependsOnScoped { singleton: String, scoped: String },
26 #[error("factory panicked while resolving service '{service}': {payload}")]
27 FactoryPanic { service: String, payload: String },
28 #[error("cannot resolve owned instance of '{0}': Singleton services cannot be owned")]
29 OwnedNotSupported(&'static str),
30 #[error(
31 "async service '{service}' cannot be built with build(); use ServiceCollection::build_async() instead"
32 )]
33 AsyncServiceRequiresBuildAsync { service: String },
34 #[error(
35 "async service '{service}' must be resolved with get_async()/get_keyed_async(); \
36 synchronous resolution is not available before its async initialization completes"
37 )]
38 AsyncServiceRequiresGetAsync { service: String },
39 #[error(
44 "synchronous singleton '{singleton}' depends on async service '{async_service}': \
45 a sync constructor cannot await; make '{singleton}' an async singleton or resolve \
46 '{async_service}' lazily in a method (ScopeFactory / get_async)"
47 )]
48 SyncSingletonDependsOnAsyncService {
49 singleton: String,
50 async_service: String,
51 },
52 #[error(
57 "scoped service '{service}' cannot be resolved from the root provider; \
58 create a scope with provider.scope() and resolve it there, or build with \
59 ServiceProviderOptions{{ validate_scopes: false }} to restore 0.7 root-scope behavior"
60 )]
61 ScopedFromRootProvider { service: String },
62 #[error("the service provider has been disposed; resolution is no longer allowed")]
64 ProviderDisposed,
65 #[error("the scope has been disposed; resolution is no longer allowed")]
67 ScopeDisposed,
68}
69
70impl RdiError {
71 pub fn not_found(ty: &'static str, context: impl Into<Option<String>>) -> Self {
73 Self::ServiceNotFound {
74 ty,
75 context: context.into(),
76 }
77 }
78
79 pub fn keyed_not_found(
81 ty: &'static str,
82 key: impl Into<String>,
83 context: impl Into<Option<String>>,
84 ) -> Self {
85 Self::KeyedServiceNotFound {
86 key: key.into(),
87 ty,
88 context: context.into(),
89 }
90 }
91
92 pub fn scoped_from_root(service: &'static str) -> Self {
94 Self::ScopedFromRootProvider {
95 service: service.to_string(),
96 }
97 }
98
99 pub fn is_circular(&self) -> bool {
108 match self {
109 Self::CircularDependency(_) => true,
110 Self::FactoryPanic { payload, .. } => payload.contains("CircularDependency"),
111 _ => false,
112 }
113 }
114
115 pub fn from_panic_payload(service: &str, payload: Box<dyn Any + Send>) -> Self {
120 match payload.downcast::<RdiError>() {
121 Ok(err) => *err,
122 Err(payload) => {
123 let msg = payload
124 .downcast_ref::<&'static str>()
125 .map(|s| (*s).to_string())
126 .or_else(|| payload.downcast_ref::<String>().cloned())
127 .unwrap_or_else(|| "<non-string panic payload>".to_string());
128 Self::FactoryPanic {
129 service: service.to_string(),
130 payload: msg,
131 }
132 }
133 }
134 }
135}
136
137#[doc(hidden)]
142pub fn inject_arc<T: Send + Sync + 'static>(
143 result: Result<std::sync::Arc<T>, RdiError>,
144) -> std::sync::Arc<T> {
145 match result {
146 Ok(v) => v,
147 Err(e) => std::panic::panic_any(e),
148 }
149}