valence_backend_surreal/
bootstrap.rs1use std::collections::BTreeSet;
4use std::sync::Arc;
5
6use valence_core::backend::DatabaseBackend;
7use valence_core::error::Result;
8use valence_core::router::DatabaseRouter;
9use valence_core::router_key::router_key;
10
11use crate::embedded::{SDb, SurrealEmbeddedBackend, ENGINE_ID};
12
13#[cfg(feature = "inventory")]
14use crate::inventory::collect_distinct_embedded_surreal_logical_names;
15
16#[derive(Clone, Copy, Debug, Default)]
18pub struct RegisterEmbeddedLogicalNamesOptions {
19 pub register_alias_engine_id: Option<&'static str>,
21}
22
23fn wrap_backend(db: SDb) -> Arc<dyn DatabaseBackend> {
24 let backend: Arc<dyn DatabaseBackend> = Arc::new(SurrealEmbeddedBackend::new(db));
25 #[cfg(feature = "instrumentation")]
26 {
27 valence_core::wrap_backend(backend)
28 }
29 #[cfg(not(feature = "instrumentation"))]
30 {
31 backend
32 }
33}
34
35fn register_backend_for_logical(
36 router: &mut DatabaseRouter,
37 logical_name: &str,
38 backend: Arc<dyn DatabaseBackend>,
39 options: RegisterEmbeddedLogicalNamesOptions,
40) {
41 let key = router_key(logical_name, ENGINE_ID);
42 router.register(key, Arc::clone(&backend));
43 if let Some(alias) = options.register_alias_engine_id {
44 let alias_key = router_key(logical_name, alias);
45 router.register(alias_key, backend);
46 }
47}
48
49pub fn register_embedded_logical_names(
51 router: &mut DatabaseRouter,
52 db: SDb,
53 logical_names: &[&str],
54 options: RegisterEmbeddedLogicalNamesOptions,
55) {
56 let backend = wrap_backend(db);
57 for &name in logical_names {
58 register_backend_for_logical(router, name, Arc::clone(&backend), options);
59 }
60}
61
62pub fn register_embedded_logical_names_slices(
64 router: &mut DatabaseRouter,
65 db: SDb,
66 groups: &[&[&str]],
67 options: RegisterEmbeddedLogicalNamesOptions,
68) {
69 let mut seen = BTreeSet::<&str>::new();
70 for group in groups {
71 for &name in *group {
72 seen.insert(name);
73 }
74 }
75 let backend = wrap_backend(db);
76 for name in seen {
77 register_backend_for_logical(router, name, Arc::clone(&backend), options);
78 }
79}
80
81pub fn register_embedded_logical_handle(
83 router: &mut DatabaseRouter,
84 logical_name: &str,
85 db: SDb,
86 options: RegisterEmbeddedLogicalNamesOptions,
87) {
88 register_embedded_logical_handles(router, &[(logical_name, db)], options);
89}
90
91pub fn register_embedded_logical_handles(
93 router: &mut DatabaseRouter,
94 handles: &[(&str, SDb)],
95 options: RegisterEmbeddedLogicalNamesOptions,
96) {
97 let mut seen = BTreeSet::<&str>::new();
98 for &(name, ref db) in handles {
99 if !seen.insert(name) {
100 continue;
101 }
102 let backend = wrap_backend(db.clone());
103 register_backend_for_logical(router, name, backend, options);
104 }
105}
106
107#[cfg(feature = "inventory")]
108pub fn register_embedded_logical_names_from_inventory(
110 router: &mut DatabaseRouter,
111 db: SDb,
112 options: RegisterEmbeddedLogicalNamesOptions,
113) {
114 let names = collect_distinct_embedded_surreal_logical_names();
115 let refs: Vec<&str> = names.iter().map(|s| s.as_str()).collect();
116 register_embedded_logical_names_slices(router, db, &[refs.as_slice()], options);
117}
118
119pub fn shared_router_with_embedded_logical_names(
121 db: SDb,
122 logical_names: &[&str],
123 options: RegisterEmbeddedLogicalNamesOptions,
124) -> Arc<DatabaseRouter> {
125 let mut router = DatabaseRouter::new();
126 register_embedded_logical_names(&mut router, db, logical_names, options);
127 Arc::new(router)
128}
129
130pub fn bootstrap_embedded_router(
153 db: SDb,
154 logical_names: &[&str],
155 options: RegisterEmbeddedLogicalNamesOptions,
156) -> Result<Arc<DatabaseRouter>> {
157 Ok(shared_router_with_embedded_logical_names(
158 db,
159 logical_names,
160 options,
161 ))
162}
163
164#[cfg(feature = "inventory")]
165pub fn bootstrap_embedded_router_from_inventory(
167 db: SDb,
168 options: RegisterEmbeddedLogicalNamesOptions,
169) -> Result<Arc<DatabaseRouter>> {
170 let mut router = DatabaseRouter::new();
171 register_embedded_logical_names_from_inventory(&mut router, db, options);
172 Ok(Arc::new(router))
173}
174
175#[cfg(test)]
176mod tests {
177 use super::*;
178 use surrealdb::engine::local::Mem;
179
180 async fn mem_db() -> SDb {
181 let db = SDb::init();
182 db.connect::<Mem>(()).await.unwrap();
183 db.use_ns("test").use_db("test").await.unwrap();
184 db
185 }
186
187 #[tokio::test]
188 async fn register_multiple_logical_names_shares_backend() {
189 let db = mem_db().await;
190 let mut router = DatabaseRouter::new();
191 register_embedded_logical_names(
192 &mut router,
193 db,
194 &["default", "billing"],
195 RegisterEmbeddedLogicalNamesOptions::default(),
196 );
197 assert_eq!(router.len().unwrap(), 2);
198 let k1 = router_key("default", ENGINE_ID);
199 let k2 = router_key("billing", ENGINE_ID);
200 assert!(router.resolve(&k1).is_ok());
201 assert!(router.resolve(&k2).is_ok());
202 }
203
204 #[tokio::test]
205 async fn alias_engine_registers_second_key() {
206 let db = mem_db().await;
207 let mut router = DatabaseRouter::new();
208 register_embedded_logical_names(
209 &mut router,
210 db,
211 &["default"],
212 RegisterEmbeddedLogicalNamesOptions {
213 register_alias_engine_id: Some("legacy_surreal"),
214 },
215 );
216 assert_eq!(router.len().unwrap(), 2);
217 }
218}