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::router::DatabaseRouter;
17use crate::router_key::router_key;
18use crate::schema::SchemaRegistry;
19use std::sync::Arc;
20use valence_telemetry::TelemetrySink;
21
22#[derive(Clone)]
24pub struct Valence {
25 pub(crate) router: Arc<DatabaseRouter>,
26 pub(crate) active_backend_key: String,
27 telemetry_sink: Arc<dyn TelemetrySink>,
28 secret_provider: Arc<dyn SecretProvider>,
29 actor_factory: Arc<dyn ActorFactory>,
30 endpoint_resolver: Arc<dyn DatabaseEndpointResolver>,
31 actor: Actor,
32 owner_override: Option<OwnerRef>,
33}
34
35impl Valence {
36 pub fn builder() -> ValenceBuilder {
37 ValenceBuilder::new()
38 }
39
40 pub fn database_router(&self) -> &Arc<DatabaseRouter> {
41 &self.router
42 }
43
44 pub fn active_backend(&self) -> Result<Arc<dyn DatabaseBackend>> {
45 self.router.resolve(&self.active_backend_key)
46 }
47
48 pub fn backend_for_table(&self, table: &str) -> Result<Arc<dyn DatabaseBackend>> {
49 let Some(meta) = SchemaRegistry::global().get_schema(table) else {
50 return self.active_backend();
51 };
52 let eval = meta.schema.database_evaluator;
53 let key = router_key(eval.logical_name(), eval.engine_id());
54 self.router
55 .resolve(&key)
56 .or_else(|_| self.active_backend())
57 .inspect_err(|_e| {
58 crate::instrumentation::metrics::record_router_resolve_error(table);
59 })
60 }
61
62 pub fn telemetry_sink(&self) -> &Arc<dyn TelemetrySink> {
63 &self.telemetry_sink
64 }
65
66 pub fn secret_provider(&self) -> &Arc<dyn SecretProvider> {
67 &self.secret_provider
68 }
69
70 pub fn actor_factory(&self) -> &Arc<dyn ActorFactory> {
71 &self.actor_factory
72 }
73
74 pub fn endpoint_resolver(&self) -> &Arc<dyn DatabaseEndpointResolver> {
75 &self.endpoint_resolver
76 }
77
78 pub fn actor(&self) -> &Actor {
79 &self.actor
80 }
81
82 pub fn with_actor(&self, actor: Actor) -> Self {
83 Self {
84 actor,
85 ..self.clone()
86 }
87 }
88
89 pub fn with_owner_override(&self, owner: OwnerRef) -> Self {
90 Self {
91 owner_override: Some(owner),
92 ..self.clone()
93 }
94 }
95
96 pub fn owner_override(&self) -> Option<&OwnerRef> {
97 self.owner_override.as_ref()
98 }
99
100 pub async fn ensure_unique_field_index(&self, table: &str, field: &str) -> Result<()> {
101 let backend = self.backend_for_table(table)?;
102 backend.define_unique_index(table, field).await
103 }
104
105 pub fn is_system(&self) -> bool {
106 self.actor.is_system()
107 }
108}
109
110impl std::fmt::Debug for Valence {
111 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
112 f.debug_struct("Valence")
113 .field("active_backend_key", &self.active_backend_key)
114 .finish_non_exhaustive()
115 }
116}
117
118#[cfg(test)]
119mod tests {
120 use super::*;
121 use crate::ports::actor::JsonActorFactory;
122
123 #[test]
124 fn builder_requires_backend() {
125 let err = Valence::builder()
126 .actor_factory(Arc::new(JsonActorFactory))
127 .build()
128 .unwrap_err();
129 assert!(err.to_string().contains("at least one backend"));
130 }
131
132 #[test]
133 fn builder_accepts_actor_with_backend() {
134 use crate::backend::DatabaseBackend;
135 use async_trait::async_trait;
136 use std::sync::atomic::{AtomicUsize, Ordering};
137
138 #[derive(Debug)]
139 struct MockBackend;
140
141 #[async_trait]
142 impl DatabaseBackend for MockBackend {
143 fn engine_id(&self) -> &'static str {
144 "mem"
145 }
146
147 fn capabilities(&self) -> crate::backend::BackendCapabilities {
148 crate::backend::BackendCapabilities::mem()
149 }
150
151 async fn execute_compiled_query(
152 &self,
153 _compiled: &crate::compiled_query::CompiledQuery,
154 ) -> crate::error::Result<Vec<serde_json::Value>> {
155 Ok(vec![])
156 }
157
158 async fn get_record(
159 &self,
160 _table: &str,
161 _id: &str,
162 ) -> crate::error::Result<Option<serde_json::Value>> {
163 static GETS: AtomicUsize = AtomicUsize::new(0);
164 GETS.fetch_add(1, Ordering::SeqCst);
165 Ok(None)
166 }
167
168 async fn create_record(
169 &self,
170 _table: &str,
171 _content: serde_json::Value,
172 ) -> crate::error::Result<serde_json::Value> {
173 Ok(serde_json::json!({}))
174 }
175
176 async fn update_record(
177 &self,
178 _table: &str,
179 _id: &str,
180 _content: serde_json::Value,
181 ) -> crate::error::Result<serde_json::Value> {
182 Ok(serde_json::json!({}))
183 }
184
185 async fn upsert_record(
186 &self,
187 _table: &str,
188 _id: &str,
189 _content: serde_json::Value,
190 ) -> crate::error::Result<serde_json::Value> {
191 Ok(serde_json::json!({}))
192 }
193
194 async fn delete_record(&self, _table: &str, _id: &str) -> crate::error::Result<()> {
195 Ok(())
196 }
197
198 async fn relate_edge(
199 &self,
200 _from: &crate::record_id::RecordId,
201 _edge_table: &str,
202 _to: &crate::record_id::RecordId,
203 ) -> crate::error::Result<()> {
204 Ok(())
205 }
206
207 async fn unrelate_edge(
208 &self,
209 _from: &crate::record_id::RecordId,
210 _edge_table: &str,
211 _to: &crate::record_id::RecordId,
212 ) -> crate::error::Result<()> {
213 Ok(())
214 }
215
216 async fn get_edge_targets(
217 &self,
218 _from: &crate::record_id::RecordId,
219 _edge_table: &str,
220 ) -> crate::error::Result<Vec<crate::record_id::RecordId>> {
221 Ok(vec![])
222 }
223 }
224
225 let v = Valence::builder()
226 .add_backend("default", Arc::new(MockBackend))
227 .with_actor(Actor::System {
228 operation: "test".into(),
229 })
230 .build()
231 .expect("build");
232 assert!(v.is_system());
233 let _ = v.active_backend().expect("backend");
234 }
235}