1use std::any::Any;
2use std::collections::HashMap;
3use std::sync::Arc;
4
5use crate::{Dependency, MicrodeError, ModuleInstanceId, Reference, RelationshipKind};
6
7#[derive(Clone)]
8pub(crate) struct ResolvedRelationship {
9 pub(crate) owner: ModuleInstanceId,
10 pub(crate) name: String,
11 pub(crate) kind: RelationshipKind,
12 pub(crate) value: Arc<dyn Any + Send + Sync>,
13}
14
15#[derive(Clone)]
16pub struct SetupContext {
17 owner: ModuleInstanceId,
18 resolutions: Arc<HashMap<u64, ResolvedRelationship>>,
19}
20
21#[derive(Clone)]
22pub struct RunContext {
23 owner: ModuleInstanceId,
24 resolutions: Arc<HashMap<u64, ResolvedRelationship>>,
25}
26
27impl SetupContext {
28 pub(crate) fn new(
29 owner: ModuleInstanceId,
30 resolutions: Arc<HashMap<u64, ResolvedRelationship>>,
31 ) -> Self {
32 Self { owner, resolutions }
33 }
34
35 pub fn use_dependency<T>(&self, relationship: &Dependency<T>) -> Result<T, MicrodeError>
36 where
37 T: Clone + Send + Sync + 'static,
38 {
39 resolve(
40 &self.owner,
41 &self.resolutions,
42 relationship.slot_id(),
43 relationship.name(),
44 Some(RelationshipKind::Dependency),
45 )
46 }
47}
48
49pub trait RunRelationship<T> {
50 fn slot_id(&self) -> u64;
51 fn name(&self) -> &str;
52}
53
54impl<T> RunRelationship<T> for Dependency<T> {
55 fn slot_id(&self) -> u64 {
56 self.slot_id()
57 }
58 fn name(&self) -> &str {
59 self.name()
60 }
61}
62
63impl<T> RunRelationship<T> for Reference<T> {
64 fn slot_id(&self) -> u64 {
65 self.slot_id()
66 }
67 fn name(&self) -> &str {
68 self.name()
69 }
70}
71
72impl RunContext {
73 pub(crate) fn new(
74 owner: ModuleInstanceId,
75 resolutions: Arc<HashMap<u64, ResolvedRelationship>>,
76 ) -> Self {
77 Self { owner, resolutions }
78 }
79
80 pub fn use_relationship<T, Slot>(&self, relationship: &Slot) -> Result<T, MicrodeError>
81 where
82 T: Clone + Send + Sync + 'static,
83 Slot: RunRelationship<T>,
84 {
85 resolve(
86 &self.owner,
87 &self.resolutions,
88 relationship.slot_id(),
89 relationship.name(),
90 None,
91 )
92 }
93}
94
95fn resolve<T>(
96 owner: &ModuleInstanceId,
97 resolutions: &HashMap<u64, ResolvedRelationship>,
98 slot_id: u64,
99 name: &str,
100 expected_kind: Option<RelationshipKind>,
101) -> Result<T, MicrodeError>
102where
103 T: Clone + Send + Sync + 'static,
104{
105 let resolved = resolutions
106 .get(&slot_id)
107 .filter(|value| &value.owner == owner)
108 .ok_or_else(|| {
109 MicrodeError::new(format!(
110 "relationship '{}.{}' is not resolved for this module",
111 owner.as_str(),
112 name
113 ))
114 })?;
115 if expected_kind.is_some_and(|kind| resolved.kind != kind) {
116 return Err(MicrodeError::new(format!(
117 "relationship '{}.{}' is not available during setup",
118 owner.as_str(),
119 resolved.name
120 )));
121 }
122 resolved.value.downcast_ref::<T>().cloned().ok_or_else(|| {
123 MicrodeError::new(format!(
124 "provider type mismatch for relationship '{}.{}'",
125 owner.as_str(),
126 resolved.name
127 ))
128 })
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134 use crate::Port;
135
136 fn resolved(
137 owner: &str,
138 name: &str,
139 kind: RelationshipKind,
140 value: Arc<dyn Any + Send + Sync>,
141 ) -> ResolvedRelationship {
142 ResolvedRelationship {
143 owner: ModuleInstanceId::new(owner),
144 name: name.to_owned(),
145 kind,
146 value,
147 }
148 }
149
150 #[test]
151 fn reports_unresolved_wrong_phase_and_provider_type_errors() {
152 let dependency = Dependency::new("database", Port::<String>::new("database"));
153 let mut values = HashMap::new();
154 values.insert(
155 dependency.slot_id(),
156 resolved(
157 "other",
158 "database",
159 RelationshipKind::Dependency,
160 Arc::new("value".to_owned()),
161 ),
162 );
163 let context = SetupContext::new(ModuleInstanceId::new("consumer"), Arc::new(values));
164 assert_eq!(
165 context.use_dependency(&dependency).unwrap_err().to_string(),
166 "relationship 'consumer.database' is not resolved for this module"
167 );
168
169 let mut values = HashMap::new();
170 values.insert(
171 dependency.slot_id(),
172 resolved(
173 "consumer",
174 "database",
175 RelationshipKind::Reference,
176 Arc::new("value".to_owned()),
177 ),
178 );
179 let context = SetupContext::new(ModuleInstanceId::new("consumer"), Arc::new(values));
180 assert_eq!(
181 context.use_dependency(&dependency).unwrap_err().to_string(),
182 "relationship 'consumer.database' is not available during setup"
183 );
184
185 let mut values = HashMap::new();
186 values.insert(
187 dependency.slot_id(),
188 resolved(
189 "consumer",
190 "database",
191 RelationshipKind::Dependency,
192 Arc::new(7_u8),
193 ),
194 );
195 let context = RunContext::new(ModuleInstanceId::new("consumer"), Arc::new(values));
196 assert_eq!(
197 context
198 .use_relationship::<String, _>(&dependency)
199 .unwrap_err()
200 .to_string(),
201 "provider type mismatch for relationship 'consumer.database'"
202 );
203 }
204}