valence_core/runtime/
mod.rs1mod builder;
4mod factory;
5
6pub use builder::ValenceBuilder;
7pub use factory::{RouterValenceFactory, RouterValenceFactoryConfig, ValenceFactory};
8
9use crate::actor::Actor;
10use crate::backend::DatabaseBackend;
11use crate::error::Result;
12use crate::owner_ref::OwnerRef;
13use crate::ports::actor::ActorFactory;
14use crate::ports::endpoints::DatabaseEndpointResolver;
15use crate::ports::secrets::SecretProvider;
16use crate::record_id::RecordId;
17use crate::request_cache::RequestPermissionCache;
18use crate::router::DatabaseRouter;
19use crate::router_key::router_key;
20use crate::schema::SchemaRegistry;
21use std::sync::Arc;
22use valence_telemetry::TelemetrySink;
23
24#[derive(Clone)]
26pub struct Valence {
27 pub(crate) router: Arc<DatabaseRouter>,
28 pub(crate) active_backend_key: String,
29 telemetry_sink: Arc<dyn TelemetrySink>,
30 secret_provider: Arc<dyn SecretProvider>,
31 actor_factory: Arc<dyn ActorFactory>,
32 endpoint_resolver: Arc<dyn DatabaseEndpointResolver>,
33 actor: Actor,
34 owner_override: Option<OwnerRef>,
35 permission_cache: Option<RequestPermissionCache>,
36}
37
38impl Valence {
39 pub fn builder() -> ValenceBuilder {
40 ValenceBuilder::new()
41 }
42
43 pub fn database_router(&self) -> &Arc<DatabaseRouter> {
44 &self.router
45 }
46
47 pub fn active_backend(&self) -> Result<Arc<dyn DatabaseBackend>> {
51 self.router.resolve(&self.active_backend_key)
52 }
53
54 pub fn backend_for_table(&self, table: &str) -> Result<Arc<dyn DatabaseBackend>> {
58 let Some(meta) = SchemaRegistry::global().get_schema(table) else {
59 return self.active_backend();
60 };
61 let eval = meta.schema.database_evaluator;
62 let key = router_key(eval.logical_name(), eval.engine_id());
63 self.router
64 .resolve(&key)
65 .or_else(|_| self.active_backend())
66 .inspect_err(|_e| {
67 crate::instrumentation::metrics::record_router_resolve_error(table);
68 })
69 }
70
71 pub fn telemetry_sink(&self) -> &Arc<dyn TelemetrySink> {
72 &self.telemetry_sink
73 }
74
75 pub fn secret_provider(&self) -> &Arc<dyn SecretProvider> {
76 &self.secret_provider
77 }
78
79 pub fn actor_factory(&self) -> &Arc<dyn ActorFactory> {
80 &self.actor_factory
81 }
82
83 pub fn endpoint_resolver(&self) -> &Arc<dyn DatabaseEndpointResolver> {
84 &self.endpoint_resolver
85 }
86
87 pub fn actor(&self) -> &Actor {
88 &self.actor
89 }
90
91 #[must_use]
98 pub fn with_actor(&self, actor: Actor) -> Self {
99 Self {
100 actor,
101 ..self.clone()
102 }
103 }
104
105 #[must_use]
111 pub fn with_owner_override(&self, owner: OwnerRef) -> Self {
112 Self {
113 owner_override: Some(owner),
114 ..self.clone()
115 }
116 }
117
118 pub fn owner_override(&self) -> Option<&OwnerRef> {
119 self.owner_override.as_ref()
120 }
121
122 pub fn permission_cache(&self) -> Option<&RequestPermissionCache> {
124 self.permission_cache.as_ref()
125 }
126
127 pub async fn get_many_to_many_target_record_ids(
137 &self,
138 from: &RecordId,
139 edge_table: &str,
140 ) -> Result<Vec<RecordId>> {
141 let backend = self.active_backend()?;
142 backend.get_edge_targets(from, edge_table).await
143 }
144
145 pub async fn relate_edge(
155 &self,
156 edge_table: &str,
157 from: &RecordId,
158 to: &RecordId,
159 ) -> Result<()> {
160 let backend = self.active_backend()?;
161 backend.relate_edge(from, edge_table, to).await
162 }
163
164 pub async fn unrelate_edge(
174 &self,
175 edge_table: &str,
176 from: &RecordId,
177 to: &RecordId,
178 ) -> Result<()> {
179 let backend = self.active_backend()?;
180 backend.unrelate_edge(from, edge_table, to).await
181 }
182
183 pub async fn ensure_unique_field_index(&self, table: &str, field: &str) -> Result<()> {
187 let backend = self.backend_for_table(table)?;
188 backend.define_unique_index(table, field).await
189 }
190
191 pub async fn ensure_ttl_for_all(&self) -> Result<()> {
200 crate::ttl::ensure_ttl_for_all(self).await
201 }
202
203 pub async fn ensure_ttl_for_table(&self, table: &str) -> Result<()> {
211 crate::ttl::ensure_ttl_for_table(self, table).await
212 }
213
214 pub async fn ensure_typed_tables_from_registry(&self) -> Result<()> {
220 crate::storage_layout::ensure_typed_tables_from_registry(self).await
221 }
222
223 pub async fn sync_typed_tables_from_registry(&self) -> Result<()> {
229 crate::storage_layout::sync_typed_tables_from_registry(self).await
230 }
231
232 pub fn is_system(&self) -> bool {
233 self.actor.is_system()
234 }
235}
236
237impl std::fmt::Debug for Valence {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
239 f.debug_struct("Valence")
240 .field("active_backend_key", &self.active_backend_key)
241 .finish_non_exhaustive()
242 }
243}
244
245#[cfg(test)]
246mod tests {
247 use super::*;
248 use crate::ports::actor::JsonActorFactory;
249
250 #[test]
251 fn builder_requires_backend() {
252 let err = Valence::builder()
253 .actor_factory(Arc::new(JsonActorFactory))
254 .build()
255 .unwrap_err();
256 assert!(err.to_string().contains("at least one backend"));
257 }
258
259 #[test]
260 fn builder_accepts_actor_with_backend() {
261 use crate::backend::DatabaseBackend;
262 use async_trait::async_trait;
263 use std::sync::atomic::{AtomicUsize, Ordering};
264
265 #[derive(Debug)]
266 struct MockBackend;
267
268 #[async_trait]
269 impl DatabaseBackend for MockBackend {
270 fn engine_id(&self) -> &'static str {
271 "mem"
272 }
273
274 fn capabilities(&self) -> crate::backend::BackendCapabilities {
275 crate::backend::BackendCapabilities::mem()
276 }
277
278 async fn execute_compiled_query(
279 &self,
280 _compiled: &crate::compiled_query::CompiledQuery,
281 ) -> crate::error::Result<Vec<serde_json::Value>> {
282 Ok(vec![])
283 }
284
285 async fn get_record(
286 &self,
287 _table: &str,
288 _id: &str,
289 ) -> crate::error::Result<Option<serde_json::Value>> {
290 static GETS: AtomicUsize = AtomicUsize::new(0);
291 GETS.fetch_add(1, Ordering::SeqCst);
292 Ok(None)
293 }
294
295 async fn create_record(
296 &self,
297 _table: &str,
298 _content: serde_json::Value,
299 ) -> crate::error::Result<serde_json::Value> {
300 Ok(serde_json::json!({}))
301 }
302
303 async fn update_record(
304 &self,
305 _table: &str,
306 _id: &str,
307 _content: serde_json::Value,
308 ) -> crate::error::Result<serde_json::Value> {
309 Ok(serde_json::json!({}))
310 }
311
312 async fn upsert_record(
313 &self,
314 _table: &str,
315 _id: &str,
316 _content: serde_json::Value,
317 ) -> crate::error::Result<serde_json::Value> {
318 Ok(serde_json::json!({}))
319 }
320
321 async fn delete_record(&self, _table: &str, _id: &str) -> crate::error::Result<()> {
322 Ok(())
323 }
324
325 async fn relate_edge(
326 &self,
327 _from: &crate::record_id::RecordId,
328 _edge_table: &str,
329 _to: &crate::record_id::RecordId,
330 ) -> crate::error::Result<()> {
331 Ok(())
332 }
333
334 async fn unrelate_edge(
335 &self,
336 _from: &crate::record_id::RecordId,
337 _edge_table: &str,
338 _to: &crate::record_id::RecordId,
339 ) -> crate::error::Result<()> {
340 Ok(())
341 }
342
343 async fn get_edge_targets(
344 &self,
345 _from: &crate::record_id::RecordId,
346 _edge_table: &str,
347 ) -> crate::error::Result<Vec<crate::record_id::RecordId>> {
348 Ok(vec![])
349 }
350 }
351
352 let v = Valence::builder()
353 .add_backend("default", Arc::new(MockBackend))
354 .with_actor(Actor::System {
355 operation: "test".into(),
356 })
357 .build()
358 .expect("build");
359 assert!(v.is_system());
360 let _ = v.active_backend().expect("backend");
361 }
362}