1use super::*;
2
3impl_veilid_log_facility!("veilid_api");
4
5pub(super) struct VeilidAPIInner {
8 context: Option<VeilidCoreContext>,
9 pub(super) debug_cache: DebugCache,
10}
11
12impl fmt::Debug for VeilidAPIInner {
13 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
14 write!(f, "VeilidAPIInner")
15 }
16}
17
18impl Drop for VeilidAPIInner {
19 fn drop(&mut self) {
20 if let Some(context) = self.context.take() {
21 spawn_detached("api shutdown", api_shutdown(context));
22 }
23 }
24}
25
26#[derive(Clone, Debug)]
40#[must_use]
41pub struct VeilidAPI {
42 inner: Arc<Mutex<VeilidAPIInner>>,
43}
44
45impl VeilidAPI {
46 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = context.log_key()), skip_all))]
47 pub(crate) fn new(context: VeilidCoreContext) -> Self {
48 veilid_log!(context debug "VeilidAPI::new()");
49 record_duration(|| Self {
50 inner: Arc::new(Mutex::new(VeilidAPIInner {
51 context: Some(context),
52 debug_cache: DebugCache {
53 imported_routes: Vec::new(),
54 opened_record_contexts: once_cell::sync::Lazy::new(
55 hashlink::LinkedHashMap::new,
56 ),
57 },
58 })),
59 })
60 }
61
62 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all))]
64 pub async fn shutdown(self) {
65 record_duration_fut(async {
66 veilid_log!(self debug "VeilidAPI::shutdown()");
67 let context = { self.inner.lock().context.take() };
68 if let Some(context) = context {
69 api_shutdown(context).await;
70 }
71 })
72 .await
73 }
74
75 #[must_use]
77 pub fn is_shutdown(&self) -> bool {
78 self.inner.lock().context.is_none()
79 }
80
81 pub fn config(&self) -> VeilidAPIResult<Arc<VeilidConfig>> {
86 let inner = self.inner.lock();
87 let Some(context) = &inner.context else {
88 return Err(VeilidAPIError::NotInitialized);
89 };
90 Ok(context.registry().config())
91 }
92
93 pub fn crypto(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, Crypto>> {
95 let inner = self.inner.lock();
96 let Some(context) = &inner.context else {
97 return Err(VeilidAPIError::NotInitialized);
98 };
99 context
100 .registry()
101 .lookup::<Crypto>()
102 .ok_or(VeilidAPIError::NotInitialized)
103 }
104
105 pub fn table_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, TableStore>> {
107 let inner = self.inner.lock();
108 let Some(context) = &inner.context else {
109 return Err(VeilidAPIError::NotInitialized);
110 };
111 context
112 .registry()
113 .lookup::<TableStore>()
114 .ok_or(VeilidAPIError::NotInitialized)
115 }
116
117 pub fn protected_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, ProtectedStore>> {
119 let inner = self.inner.lock();
120 let Some(context) = &inner.context else {
121 return Err(VeilidAPIError::NotInitialized);
122 };
123 context
124 .registry()
125 .lookup::<ProtectedStore>()
126 .ok_or(VeilidAPIError::NotInitialized)
127 }
128
129 #[cfg(feature = "unstable-blockstore")]
131 pub fn block_store(&self) -> VeilidAPIResult<VeilidComponentGuard<'_, BlockStore>> {
132 let inner = self.inner.lock();
133 let Some(context) = &inner.context else {
134 return Err(VeilidAPIError::NotInitialized);
135 };
136 context
137 .registry()
138 .lookup::<BlockStore>()
139 .ok_or(VeilidAPIError::NotInitialized)
140 }
141
142 pub(crate) fn core_context(&self) -> VeilidAPIResult<VeilidCoreContext> {
146 let inner = self.inner.lock();
147 let Some(context) = &inner.context else {
148 return Err(VeilidAPIError::NotInitialized);
149 };
150 Ok(context.clone())
151 }
152
153 pub(crate) fn with_debug_cache<R, F: FnOnce(&mut DebugCache) -> R>(&self, callback: F) -> R {
154 let mut inner = self.inner.lock();
155 callback(&mut inner.debug_cache)
156 }
157
158 #[must_use]
159 pub(crate) fn log_key(&self) -> &str {
160 let inner = self.inner.lock();
161 let Some(context) = &inner.context else {
162 return "";
163 };
164 context.log_key()
165 }
166
167 #[expect(clippy::unused_async)]
172 pub async fn get_state(&self) -> VeilidAPIResult<VeilidState> {
173 let attachment_manager = self.core_context()?.attachment_manager();
174 let network_manager = attachment_manager.network_manager();
175 let config = self.config()?;
176
177 let attachment = attachment_manager.get_veilid_state();
178 let network = network_manager.get_veilid_state();
179
180 Ok(VeilidState {
181 attachment,
182 network,
183 config: Box::new(VeilidStateConfig {
184 config: config.as_ref().clone(),
185 }),
186 })
187 }
188
189 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
191 pub async fn attach(&self) -> VeilidAPIResult<()> {
192 record_duration_fut(async {
193 veilid_log!(self debug
194 "VeilidAPI::attach()");
195 let attachment_manager = self.core_context()?.attachment_manager();
196 if !Box::pin(attachment_manager.attach()).await {
197 apibail_generic!("Already attached");
198 }
199 Ok(())
200 })
201 .await
202 .inspect_err(log_veilid_api_error!(self))
203 }
204
205 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
207 pub async fn detach(&self) -> VeilidAPIResult<()> {
208 record_duration_fut(async {
209 veilid_log!(self debug
210 "VeilidAPI::detach()");
211
212 let attachment_manager = self.core_context()?.attachment_manager();
213 if !Box::pin(attachment_manager.detach()).await {
214 apibail_generic!("Already detached");
215 }
216 Ok(())
217 })
218 .await
219 .inspect_err(log_veilid_api_error!(self))
220 }
221
222 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip_all, ret))]
227 pub fn routing_context(&self) -> VeilidAPIResult<RoutingContext> {
228 record_duration(|| {
229 veilid_log!(self debug
230 "VeilidAPI::routing_context()");
231
232 RoutingContext::try_new(self.clone())
233 })
234 .inspect_err(log_veilid_api_error!(self))
235 }
236
237 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
243 pub fn get_dht_record_key(
244 &self,
245 schema: DHTSchema,
246 owner_key: PublicKey,
247 encryption_key: Option<SharedSecret>,
248 ) -> VeilidAPIResult<RecordKey> {
249 record_duration(|| {
250 veilid_log!(self debug
251 "RoutingContext::get_dht_record_key(self: {:?} schema: {:?}, owner_key: {:?}, encryption_key: {:?}", self, schema, owner_key, encryption_key);
252 schema.validate()?;
253
254 self.crypto()?.check_public_key(&owner_key)?;
255 if let Some(encryption_key) = encryption_key.as_ref() {
256 self.crypto()?.check_shared_secret(encryption_key)?;
257 }
258
259 let storage_manager = self.core_context()?.storage_manager();
260 storage_manager.get_record_key(schema, &owner_key, encryption_key)
261 }).inspect_err(log_veilid_api_error!(self))
262 }
263
264 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", skip(self), fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
266 pub fn generate_member_id(&self, writer_key: &PublicKey) -> VeilidAPIResult<MemberId> {
267 record_duration(|| {
268 veilid_log!(self debug "VeilidAPI::generate_member_id(writer_key: {:?}", writer_key);
269
270 self.crypto()?.check_public_key(writer_key)?;
271
272 let storage_manager = self.core_context()?.storage_manager();
273 storage_manager.generate_member_id(writer_key)
274 })
275 .inspect_err(log_veilid_api_error!(self))
276 }
277
278 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), ret))]
283 pub async fn transact_dht_records(
284 &self,
285 record_keys: Vec<RecordKey>,
286 options: Option<TransactDHTRecordsOptions>,
287 ) -> VeilidAPIResult<DHTTransaction> {
288 record_duration_fut(async {
289 veilid_log!(self debug
290 "VeilidAPI::transact_dht_records(self: {:?}, record_keys: {:?}, options: {:?})", self, record_keys, options);
291
292 let storage_manager = self.core_context()?.storage_manager();
293
294 for record_key in &record_keys {
295 storage_manager.check_record_key(record_key)?;
296 }
297
298 let handle = Box::pin(storage_manager.begin_transaction(record_keys, options)).await?;
299
300 DHTTransaction::new(self.clone(), handle.clone())
301 }).await.inspect_err(log_veilid_api_error!(self))
302 }
303
304 pub async fn new_private_route(&self) -> VeilidAPIResult<RouteBlob> {
315 record_duration_fut(async {
316 Box::pin(self.new_custom_private_route(
317 &VALID_CRYPTO_KINDS,
318 Stability::Reliable,
319 Sequencing::PreferOrdered,
320 ))
321 .await
322 })
323 .await
324 }
325
326 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
335 pub async fn new_custom_private_route(
336 &self,
337 crypto_kinds: &[CryptoKind],
338 stability: Stability,
339 sequencing: Sequencing,
340 ) -> VeilidAPIResult<RouteBlob> {
341 record_duration_fut(async {
342 veilid_log!(self debug
343 "VeilidAPI::new_custom_private_route(crypto_kinds: {:?}, stability: {:?}, sequencing: {:?})",
344 crypto_kinds,
345 stability,
346 sequencing);
347
348 for kind in crypto_kinds {
349 Crypto::validate_crypto_kind(*kind)?;
350 }
351
352 let default_route_hop_count: usize =
353 self.config()?.network.rpc.default_route_hop_count.into();
354
355 let safety_spec = SafetySpec {
356 preferred_route: None,
357 hop_count: default_route_hop_count,
358 stability,
359 sequencing,
360 };
361
362 let routing_table = self.core_context()?.routing_table();
363 let rss = routing_table.route_spec_store();
364 let route_id =
365 rss.allocate_route(crypto_kinds, &safety_spec, DirectionSet::all(), &[], false)?;
366 match Box::pin(rss.test_route(route_id.clone())).await? {
367 Some(true) => {
368 }
370 Some(false) => {
371 rss.release_route(route_id.clone());
372 apibail_try_again!("allocated route failed to test");
373 }
374 None => {
375 rss.release_route(route_id.clone());
376 apibail_try_again!("allocated route could not be tested");
377 }
378 }
379 let private_routes = rss.assemble_private_route_set(&route_id, Some(true))?;
380 let blob = match RouteSpecStore::private_routes_to_blob(&private_routes) {
381 Ok(v) => v,
382 Err(e) => {
383 rss.release_route(route_id);
384 return Err(e);
385 }
386 };
387
388 rss.mark_route_published(&route_id, true)?;
389
390 Ok(RouteBlob { route_id, blob })
391 }).await.inspect_err(log_veilid_api_error!(self))
392 }
393
394 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
398 pub fn import_remote_private_route(&self, blob: Vec<u8>) -> VeilidAPIResult<RouteId> {
399 record_duration(|| {
400 veilid_log!(self debug
401 "VeilidAPI::import_remote_private_route(blob: {:?})", blob);
402 let routing_table = self.core_context()?.routing_table();
403 let rss = routing_table.route_spec_store();
404 rss.import_remote_route_blob(blob)
405 })
406 .inspect_err(log_veilid_api_error!(self))
407 }
408
409 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
414 pub fn release_private_route(&self, route_id: RouteId) -> VeilidAPIResult<()> {
415 record_duration(|| {
416 veilid_log!(self debug
417 "VeilidAPI::release_private_route(route_id: {:?})", route_id);
418
419 let routing_table = self.core_context()?.routing_table();
420 routing_table.check_route_id(&route_id)?;
421
422 let rss = routing_table.route_spec_store();
423 if !rss.release_route(route_id.clone()) {
424 apibail_invalid_argument!("release_private_route", "key", route_id);
425 }
426 Ok(())
427 })
428 .inspect_err(log_veilid_api_error!(self))
429 }
430
431 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
439 pub async fn app_call_reply(
440 &self,
441 call_id: OperationId,
442 message: Vec<u8>,
443 ) -> VeilidAPIResult<()> {
444 record_duration_fut(async {
445 veilid_log!(self debug
446 "VeilidAPI::app_call_reply(call_id: {:?}, message_len: {})", call_id, message.len());
447 veilid_log!(self trace "message: {:?}", message);
448
449 let rpc_processor = self.core_context()?.rpc_processor();
450 rpc_processor
451 .app_call_reply(call_id, message)
452 .map_err(|e| e.into())
453 }).await.inspect_err(log_veilid_api_error!(self))
454 }
455
456 #[cfg(feature = "unstable-tunnels")]
460 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
461 pub async fn start_tunnel(
462 &self,
463 _endpoint_mode: TunnelMode,
464 _depth: u8,
465 ) -> VeilidAPIResult<PartialTunnel> {
466 panic!("unimplemented");
467 }
468
469 #[cfg(feature = "unstable-tunnels")]
470 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
471 pub async fn complete_tunnel(
472 &self,
473 _endpoint_mode: TunnelMode,
474 _depth: u8,
475 _partial_tunnel: PartialTunnel,
476 ) -> VeilidAPIResult<FullTunnel> {
477 panic!("unimplemented");
478 }
479
480 #[cfg(feature = "unstable-tunnels")]
481 #[cfg_attr(feature = "instrument", instrument(target = "veilid_api", level = "debug", fields(duration, __VEILID_LOG_KEY = self.log_key()), skip(self), ret))]
482 pub async fn cancel_tunnel(&self, _tunnel_id: TunnelId) -> VeilidAPIResult<bool> {
483 panic!("unimplemented");
484 }
485}