Skip to main content

ferroui_dependency_injection/
service_type.rs

1//! `ServiceLifetime.cs`, `IServiceProviderIsService.cs`, `IServiceProviderIsKeyedService.cs`,
2//! `IKeyedServiceProvider.cs`, `KeyedService.cs` and the Rust service identifiers.
3
4use 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/// An arbitrary value key, compared by type and value. `None` represents an unkeyed service.
73#[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    /// Catch-all registration / query for all explicitly keyed registrations.
80    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 {}