flow_di/
service_key.rs

1use std::any::TypeId;
2use std::fmt::{Debug, Display, Formatter};
3use std::hash::{Hash, Hasher};
4
5/// Service key for identifying and distinguishing different services
6/// Supports both type-based keys and named keys
7#[derive(Clone)]
8pub enum ServiceKey {
9    /// Type-based key - uses TypeId as unique identifier
10    Type(TypeId),
11
12    /// Name-based key - combines TypeId and name as unique identifier
13    Named { type_id: TypeId, name: String },
14}
15
16impl ServiceKey {
17    /// Create a type-based service key
18    pub fn of_type<T: 'static>() -> Self {
19        ServiceKey::Type(TypeId::of::<T>())
20    }
21
22    /// Create a name-based service key
23    pub fn named<T: 'static>(name: impl Into<String>) -> Self {
24        ServiceKey::Named {
25            type_id: TypeId::of::<T>(),
26            name: name.into(),
27        }
28    }
29
30    /// Get type ID
31    pub fn type_id(&self) -> TypeId {
32        match self {
33            ServiceKey::Type(type_id) => *type_id,
34            ServiceKey::Named { type_id, .. } => *type_id,
35        }
36    }
37
38    /// Get name (if any)
39    pub fn name(&self) -> Option<&str> {
40        match self {
41            ServiceKey::Type(_) => None,
42            ServiceKey::Named { name, .. } => Some(name),
43        }
44    }
45
46    /// Check if it's a type-based key
47    pub fn is_named(&self) -> bool {
48        matches!(self, ServiceKey::Named { .. })
49    }
50
51    /// Get type name
52    pub fn type_name(&self) -> &'static str {
53        match self {
54            ServiceKey::Type(_type_id) => {
55                // Cannot get type name directly from TypeId here, but can use other methods
56                "Unknown"
57            }
58            ServiceKey::Named { .. } => "Unknown",
59        }
60    }
61}
62
63impl PartialEq for ServiceKey {
64    fn eq(&self, other: &Self) -> bool {
65        match (self, other) {
66            (ServiceKey::Type(a), ServiceKey::Type(b)) => a == b,
67            (
68                ServiceKey::Named {
69                    type_id: a_type,
70                    name: a_name,
71                },
72                ServiceKey::Named {
73                    type_id: b_type,
74                    name: b_name,
75                },
76            ) => a_type == b_type && a_name == b_name,
77            _ => false,
78        }
79    }
80}
81
82impl Eq for ServiceKey {}
83
84impl Hash for ServiceKey {
85    fn hash<H: Hasher>(&self, state: &mut H) {
86        match self {
87            ServiceKey::Type(type_id) => {
88                0u8.hash(state); // Distinguish enum variant
89                type_id.hash(state);
90            }
91            ServiceKey::Named { type_id, name } => {
92                1u8.hash(state); // Distinguish enum variant
93                type_id.hash(state);
94                name.hash(state);
95            }
96        }
97    }
98}
99
100impl Debug for ServiceKey {
101    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
102        match self {
103            ServiceKey::Type(type_id) => {
104                write!(f, "ServiceKey::Type({type_id:?})")
105            }
106            ServiceKey::Named { type_id, name } => {
107                write!(f, "ServiceKey::Named(type: {type_id:?}, name: '{name}')")
108            }
109        }
110    }
111}
112
113impl Display for ServiceKey {
114    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
115        match self {
116            ServiceKey::Type(_) => {
117                write!(f, "Type Service")
118            }
119            ServiceKey::Named { name, .. } => {
120                write!(f, "Named Service '{name}'")
121            }
122        }
123    }
124}
125
126/// Convenient macro for creating service keys
127#[macro_export]
128macro_rules! service_key {
129    ($type:ty) => {
130        $crate::ServiceKey::of_type::<$type>()
131    };
132    ($type:ty, $name:expr) => {
133        $crate::ServiceKey::named::<$type>($name)
134    };
135}
136
137#[cfg(test)]
138mod tests {
139    use super::*;
140
141    struct TestService;
142    struct AnotherService;
143
144    #[test]
145    fn test_type_key_creation() {
146        let key1 = ServiceKey::of_type::<TestService>();
147        let key2 = ServiceKey::of_type::<TestService>();
148        let key3 = ServiceKey::of_type::<AnotherService>();
149
150        assert_eq!(key1, key2);
151        assert_ne!(key1, key3);
152        assert!(!key1.is_named());
153        assert!(key1.name().is_none());
154    }
155
156    #[test]
157    fn test_named_key_creation() {
158        let key1 = ServiceKey::named::<TestService>("primary");
159        let key2 = ServiceKey::named::<TestService>("primary");
160        let key3 = ServiceKey::named::<TestService>("secondary");
161        let key4 = ServiceKey::named::<AnotherService>("primary");
162
163        assert_eq!(key1, key2);
164        assert_ne!(key1, key3);
165        assert_ne!(key1, key4);
166        assert!(key1.is_named());
167        assert_eq!(key1.name(), Some("primary"));
168    }
169
170    #[test]
171    fn test_type_vs_named_key() {
172        let type_key = ServiceKey::of_type::<TestService>();
173        let named_key = ServiceKey::named::<TestService>("test");
174
175        assert_ne!(type_key, named_key);
176        assert_eq!(type_key.type_id(), named_key.type_id());
177    }
178
179    #[test]
180    fn test_macro() {
181        let key1 = service_key!(TestService);
182        let key2 = service_key!(TestService, "named");
183
184        assert_eq!(key1, ServiceKey::of_type::<TestService>());
185        assert_eq!(key2, ServiceKey::named::<TestService>("named"));
186    }
187}