ferroui_dependency_injection/
service_type.rs1use std::any::{Any, TypeId};
5use std::fmt;
6use std::hash::{Hash, Hasher};
7use std::rc::Rc;
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
10#[repr(i32)]
11pub enum ServiceLifetime {
12 Singleton = 0,
13 Scoped = 1,
14 Transient = 2,
15}
16
17#[derive(Clone, Copy, Debug)]
18pub struct ServiceType {
19 id: TypeId,
20 name: &'static str,
21}
22impl ServiceType {
23 pub fn of<S: ?Sized + 'static>() -> Self {
24 Self {
25 id: TypeId::of::<S>(),
26 name: std::any::type_name::<S>(),
27 }
28 }
29 #[cfg(feature = "splat")]
30 pub(crate) fn from_parts(id: TypeId, name: &'static str) -> Self {
31 Self { id, name }
32 }
33 pub fn id(self) -> TypeId {
34 self.id
35 }
36 pub fn name(self) -> &'static str {
37 self.name
38 }
39}
40impl PartialEq for ServiceType {
41 fn eq(&self, other: &Self) -> bool {
42 self.id == other.id
43 }
44}
45impl Eq for ServiceType {}
46impl Hash for ServiceType {
47 fn hash<H: Hasher>(&self, state: &mut H) {
48 self.id.hash(state);
49 }
50}
51
52trait KeyValue: fmt::Debug {
53 fn as_any(&self) -> &dyn Any;
54 fn equals(&self, other: &dyn KeyValue) -> bool;
55 fn hash_value(&self) -> u64;
56}
57impl<T: Eq + Hash + fmt::Debug + 'static> KeyValue for T {
58 fn as_any(&self) -> &dyn Any {
59 self
60 }
61 fn equals(&self, other: &dyn KeyValue) -> bool {
62 other.as_any().downcast_ref::<T>() == Some(self)
63 }
64 fn hash_value(&self) -> u64 {
65 let mut h = std::collections::hash_map::DefaultHasher::new();
66 TypeId::of::<T>().hash(&mut h);
67 self.hash(&mut h);
68 h.finish()
69 }
70}
71
72#[derive(Clone)]
74pub struct ServiceKey(Option<Rc<dyn KeyValue>>);
75impl ServiceKey {
76 pub fn new<T: Eq + Hash + fmt::Debug + 'static>(value: T) -> Self {
77 Self(Some(Rc::new(value)))
78 }
79 pub const ANY: Self = Self(None);
81 pub fn is_any(&self) -> bool {
82 self.0.is_none()
83 }
84 pub fn downcast_ref<T: 'static>(&self) -> Option<&T> {
85 self.0.as_ref()?.as_any().downcast_ref()
86 }
87}
88impl From<&str> for ServiceKey {
89 fn from(s: &str) -> Self {
90 Self::new(s.to_owned())
91 }
92}
93impl From<String> for ServiceKey {
94 fn from(s: String) -> Self {
95 Self::new(s)
96 }
97}
98impl PartialEq for ServiceKey {
99 fn eq(&self, other: &Self) -> bool {
100 match (&self.0, &other.0) {
101 (Some(a), Some(b)) => a.equals(&**b),
102 (None, None) => true,
103 _ => false,
104 }
105 }
106}
107impl Eq for ServiceKey {}
108impl Hash for ServiceKey {
109 fn hash<H: Hasher>(&self, state: &mut H) {
110 self.is_any().hash(state);
111 if let Some(v) = &self.0 {
112 v.hash_value().hash(state);
113 }
114 }
115}
116impl fmt::Debug for ServiceKey {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 match &self.0 {
119 Some(v) => fmt::Debug::fmt(v, f),
120 None => f.write_str("AnyKey"),
121 }
122 }
123}
124
125#[derive(Clone, Debug, PartialEq, Eq, Hash)]
126pub struct ServiceIdentifier {
127 pub service_type: ServiceType,
128 pub service_key: Option<ServiceKey>,
129}
130impl ServiceIdentifier {
131 pub fn of<S: ?Sized + 'static>() -> Self {
132 Self {
133 service_type: ServiceType::of::<S>(),
134 service_key: None,
135 }
136 }
137 pub fn keyed<S: ?Sized + 'static>(key: Option<ServiceKey>) -> Self {
138 Self {
139 service_type: ServiceType::of::<S>(),
140 service_key: key,
141 }
142 }
143}
144
145pub type ErasedService = Rc<dyn Any>;
146pub fn erase_service<S: ?Sized + 'static>(value: Rc<S>) -> ErasedService {
147 Rc::new(value)
148}
149pub fn unerase_service<S: ?Sized + 'static>(value: &ErasedService) -> Option<Rc<S>> {
150 value.downcast_ref::<Rc<S>>().cloned()
151}
152pub type ServiceResult<T> = Result<T, ServiceError>;
153
154#[derive(Clone, Debug, PartialEq, Eq)]
155pub enum ServiceError {
156 MissingService(ServiceIdentifier),
157 CircularDependency(Vec<ServiceIdentifier>),
158 ScopedFromRoot(ServiceIdentifier),
159 ScopedInSingleton(ServiceIdentifier),
160 Disposed,
161 AsyncDisposalRequired,
162 AnyKeySingleResolution,
163 TypeMismatch(ServiceType),
164 Factory(String),
165 Validation(Vec<ServiceError>),
166}
167impl fmt::Display for ServiceError {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 Self::MissingService(id) => write!(f, "No service registered for {id:?}"),
171 Self::CircularDependency(path) => write!(f, "Circular dependency: {path:?}"),
172 Self::ScopedFromRoot(id) => write!(
173 f,
174 "Cannot resolve scoped service {id:?} from the root provider"
175 ),
176 Self::ScopedInSingleton(id) => {
177 write!(f, "Cannot consume scoped service {id:?} from a singleton")
178 }
179 Self::Disposed => f.write_str("The service provider or scope has been disposed"),
180 Self::AsyncDisposalRequired => {
181 f.write_str("An async-only service requires dispose_async")
182 }
183 Self::AnyKeySingleResolution => {
184 f.write_str("AnyKey may only be used to resolve multiple services")
185 }
186 Self::TypeMismatch(t) => {
187 write!(f, "Factory returned a value of the wrong type for {t:?}")
188 }
189 Self::Factory(message) => f.write_str(message),
190 Self::Validation(errors) => write!(f, "Service validation failed: {errors:?}"),
191 }
192 }
193}
194impl std::error::Error for ServiceError {}