polydat_core/resource.rs
1// Copyright 2024-2026 Jonathan Shook
2// SPDX-License-Identifier: Apache-2.0
3
4//! Dependency-inverted resource-accessor bridge (SRD-104, Phase 0).
5//!
6//! A polydat kernel node sometimes needs a **live, host-owned resource**
7//! (the first consumer is a CQL `Session`) addressed by its configuration
8//! **fingerprint**. polydat is the dependency floor — it must not depend on
9//! the host runtime — so the bridge is expressed here as a **type-erased**
10//! trait plus a process-global install point:
11//!
12//! - The host (nbrs-runtime's resource pool) implements [`ResourceAccessor`]
13//! and installs it into [`RESOURCE_ACCESSOR`] once at session start. The
14//! global is only a *bridge* to the pool; the pool remains the single,
15//! definitive owner of the resource.
16//! - A polydat node reaches a resource by calling [`resource_lookup`], which
17//! consults the installed accessor. The payload is erased to
18//! `Arc<dyn Any + Send + Sync>` so polydat needs no host types; the
19//! consuming node downcasts it to its own concrete handle.
20//!
21//! `eval` is synchronous and context-free, so a node cannot be handed a
22//! service reference or await a connect. This process-global registry is the
23//! established nb-rs pattern for a node reaching a live resource (the same
24//! shape dataset handles use). The lookup is a pure synchronous read of what
25//! the host has already attached; vivification timing is the host's concern.
26
27use std::any::Any;
28use std::sync::{Arc, OnceLock};
29
30/// Type-erased accessor over the host's live resource store.
31///
32/// Implemented by the host runtime (nbrs-runtime's resource pool) and
33/// installed into [`RESOURCE_ACCESSOR`]. The trait is deliberately minimal
34/// and free of host types so polydat stays the dependency floor.
35pub trait ResourceAccessor: Send + Sync {
36 /// Synchronous lookup of an already-attached resource's accessor
37 /// payload by fingerprint `key`. Returns `None` when no live entry
38 /// matches that key (never blocks, never connects).
39 ///
40 /// The `key` is the host's stable rendering of a resource fingerprint;
41 /// a single canonical rendering is shared by whoever installs the
42 /// payload and whoever looks it up, so the string round-trips exactly.
43 fn lookup(&self, key: &str) -> Option<Arc<dyn Any + Send + Sync>>;
44}
45
46/// Process-global bridge to the host's resource accessor, installed once by
47/// the runtime at session start. `None` (uninstalled) is the norm for any
48/// polydat use that has no host — the trait is a bridge, not a requirement.
49pub static RESOURCE_ACCESSOR: OnceLock<Arc<dyn ResourceAccessor>> = OnceLock::new();
50
51/// Look up an already-attached resource's accessor payload by fingerprint
52/// `key` through the installed [`RESOURCE_ACCESSOR`].
53///
54/// Returns `None` when no accessor is installed (no host) or when no live
55/// entry matches `key`. Consuming nodes downcast the returned
56/// `Arc<dyn Any + Send + Sync>` to their concrete handle type.
57pub fn resource_lookup(key: &str) -> Option<Arc<dyn Any + Send + Sync>> {
58 RESOURCE_ACCESSOR.get()?.lookup(key)
59}