harmos_sdk/operations/
dependencies.rs1use std::any::{Any, TypeId, type_name};
18use std::collections::HashMap;
19use std::sync::{Arc, OnceLock};
20
21use super::CallContext;
25
26#[derive(Clone, Default)]
35pub struct Dependencies {
36 entries: Arc<HashMap<TypeId, Arc<dyn Any + Send + Sync>>>,
37}
38
39#[derive(Clone)]
40pub struct ExecutionContext {
41 dependencies: Dependencies,
42 pub call: CallContext,
43}
44
45#[derive(Clone, Debug, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
46pub struct AssignedOperation {
47 pub operation: String,
48 pub fingerprint: [u8; 32],
49 pub generation: u64,
50}
51
52#[derive(Clone, Default)]
53pub struct OperationAssignments {
54 entries: Arc<OnceLock<Vec<AssignedOperation>>>,
55}
56
57#[derive(Debug, thiserror::Error)]
60pub enum ResourceError {
61 #[error("resource `{name}` is already present")]
62 Duplicate { name: &'static str },
63
64 #[error("resource `{name}` is missing")]
65 Missing { name: &'static str },
66}
67
68impl Dependencies {
73 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn insert<T: Send + Sync + 'static>(&mut self, value: Arc<T>) -> Result<(), ResourceError> {
78 let key = TypeId::of::<T>();
79 if self.entries.contains_key(&key) {
80 return Err(ResourceError::Duplicate { name: type_name::<T>() });
81 }
82 Arc::make_mut(&mut self.entries).insert(key, value);
83 Ok(())
84 }
85
86 pub fn resource<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, ResourceError> {
87 self.entries
88 .get(&TypeId::of::<T>())
89 .cloned()
90 .and_then(|entry| entry.downcast().ok())
91 .ok_or(ResourceError::Missing { name: type_name::<T>() })
92 }
93
94 pub fn contains(&self, resource: TypeId) -> bool {
95 self.entries.contains_key(&resource)
96 }
97}
98
99impl ExecutionContext {
100 pub fn new(dependencies: Dependencies, call: CallContext) -> Self {
101 Self { dependencies, call }
102 }
103
104 pub fn resource<T: Send + Sync + 'static>(&self) -> Result<Arc<T>, ResourceError> {
105 self.dependencies.resource::<T>()
106 }
107
108 pub fn dependencies(&self) -> &Dependencies {
109 &self.dependencies
110 }
111
112 pub(crate) fn for_registration(&self, mut dependencies: Dependencies) -> Self {
113 Arc::make_mut(&mut dependencies.entries).extend(self.dependencies.entries.iter().map(|(key, value)| (*key, value.clone())));
115 Self::new(dependencies, self.call.clone())
116 }
117}
118
119impl AssignedOperation {
120 pub fn encode(entries: &[Self]) -> Result<Vec<u8>, super::ServingError> {
121 postcard::to_allocvec(entries).map_err(|_| super::ServingError::Catalog)
122 }
123}
124
125impl OperationAssignments {
126 pub fn install(&self, bytes: &[u8]) -> Result<(), super::ServingError> {
127 let entries: Vec<AssignedOperation> =
128 if bytes.is_empty() { Vec::new() } else { postcard::from_bytes(bytes).map_err(|_| super::ServingError::Catalog)? };
129 let mut locals = std::collections::BTreeSet::new();
130 for entry in &entries {
131 harmos_domain::QualifiedId::parse(&entry.operation).map_err(|_| super::ServingError::Catalog)?;
132 let (_, local) = entry.operation.split_once('.').ok_or(super::ServingError::Catalog)?;
133 if entry.generation == 0 || !locals.insert(local) {
134 return Err(super::ServingError::Catalog);
135 }
136 }
137 if let Some(original) = self.entries.get() {
138 return if original == &entries { Ok(()) } else { Err(super::ServingError::Catalog) };
139 }
140 self.entries.set(entries).map_err(|_| super::ServingError::Catalog)
141 }
142
143 pub fn resolve<O: super::OperationDeclaration>(&self) -> Result<&AssignedOperation, super::ServingError> {
144 self.resolve_target(&super::Target::of::<O>())
145 }
146
147 pub fn resolve_target(&self, target: &super::Target) -> Result<&AssignedOperation, super::ServingError> {
148 let local = |entry: &&AssignedOperation| entry.operation.split_once('.').is_some_and(|(_, local)| local == target.id);
149 let entry = self
150 .entries
151 .get()
152 .and_then(|entries| entries.iter().find(local))
153 .ok_or(super::ServingError::Unassigned { operation: target.id })?;
154 if entry.fingerprint != (target.definition)().fingerprint.0 {
155 return Err(super::ServingError::Identity { operation: entry.operation.clone() });
156 }
157 Ok(entry)
158 }
159}
160
161#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn snapshots_share_values_and_isolate_insertions() {
173 let mut dependencies = Dependencies::new();
174 let value = Arc::new(7u32);
175 dependencies.insert(value.clone()).unwrap();
176 let snapshot = dependencies.clone();
177 dependencies.insert(Arc::new(9u64)).unwrap();
178 assert!(Arc::ptr_eq(&snapshot.resource::<u32>().unwrap(), &value));
179 assert!(matches!(snapshot.resource::<u64>(), Err(ResourceError::Missing { .. })));
180 assert!(matches!(dependencies.insert(Arc::new(8u32)), Err(ResourceError::Duplicate { .. })));
181 assert_eq!(*dependencies.resource::<u32>().unwrap(), 7);
182 }
183}