pub struct Graph { /* private fields */ }Expand description
Graph container — the one Core-free namespace + mount-tree handle (canonical §3, D246).
Clone is a cheap Rc bump; a subgraph (from mount*/ancestors)
is just a Graph into a child level. Intentionally !Send + !Sync
(D246/S2c single-owner): no Core, no borrow — captured into
owner-side sinks on the one thread that owns the Core. Pass the
embedder’s &Core (e.g. owned.core()) into every Core-touching
method.
Implementations§
Source§impl Graph
impl Graph
Sourcepub fn new(name: impl Into<String>) -> Self
pub fn new(name: impl Into<String>) -> Self
Construct a named, empty root graph. D246: the graph is
Core-free — the embedder owns the Core (see
graphrefly_core::OwnedCore) and passes &Core into
Core-touching ops.
Sourcepub fn tag_factory(
&self,
factory: impl Into<String>,
factory_args: Option<Value>,
)
pub fn tag_factory( &self, factory: impl Into<String>, factory_args: Option<Value>, )
R3.1.2 — annotate the graph with the factory function name + args
used to construct it. Provenance for [describe()], snapshot
replay, and debugging. Surfaces at the top of describe() output
as factory + factoryArgs keys.
Invariant (pure-ts QA F8, graph.ts:1686-1689):
a second call WITHOUT factory_args MUST clear stale args
(re-assignment to None, not a no-op) — otherwise
tag_factory("a", {...}) then tag_factory("b") would pair
"b" with {...} and report mismatched provenance.
Cross-arm spec citation: canonical R3.1.2 at
docs/implementation-plan-13.6-canonical-spec.md:768
(graphrefly-ts). Drops the spec’s this-chain return per the
D267 / D282 async-everywhere Impl convention — every dispatcher-
touching parity method returns the post-call observable shape, not
a chainable handle.
No reactive emission. Pure-ts bumps _topologyVersion for
SPEC-PERSISTENCE bookkeeping (DS-14.5.A Q1), but explicitly emits
no TopologyEvent. Rust has no equivalent bookkeeping field —
since describe() reads factory / factory_args fresh on every
call, subsequent topology events observe the latest tag
automatically (no cache invalidation needed). Parity test
tag-factory.test.ts:96-140 pins this contract cross-arm.
Sourcepub fn is_valid_name(name: &str) -> bool
pub fn is_valid_name(name: &str) -> bool
Whether name is a legal local node/subgraph name.
Sourcepub fn subscribe_namespace_change(&self, sink: Rc<dyn Fn(&Core)>) -> u64
pub fn subscribe_namespace_change(&self, sink: Rc<dyn Fn(&Core)>) -> u64
Subscribe to namespace changes (add, remove, mount, unmount,
destroy). The sink fires AFTER the inner lock is dropped, with
the owner’s &Core. Returns a subscription id.
Sourcepub fn unsubscribe_namespace_change(&self, id: u64)
pub fn unsubscribe_namespace_change(&self, id: u64)
Unsubscribe a namespace-change sink by id (owner-invoked, D246 rule 3 — no RAII below the binding).
Sourcepub fn describe(&self, core: &Core) -> GraphDescribeOutput
pub fn describe(&self, core: &Core) -> GraphDescribeOutput
Snapshot the graph’s topology + lifecycle state (JSON form).
value fields are raw u64 handles.
Sourcepub fn describe_with_debug(
&self,
core: &Core,
debug: &dyn DebugBindingBoundary,
) -> GraphDescribeOutput
pub fn describe_with_debug( &self, core: &Core, debug: &dyn DebugBindingBoundary, ) -> GraphDescribeOutput
Self::describe with each value rendered via debug.
Sourcepub fn describe_reactive(
&self,
core: &Core,
sink: &DescribeSink,
) -> ReactiveDescribeHandle
pub fn describe_reactive( &self, core: &Core, sink: &DescribeSink, ) -> ReactiveDescribeHandle
Subscribe to live topology snapshots (canonical §3.6.1
reactive: true). Push-on-subscribe + re-fires on namespace
changes and set_deps. D246 rule 3: returns an id-bearing
handle with an explicit owner-invoked
ReactiveDescribeHandle::detach — no RAII Drop.
Sourcepub fn observe(&self, path: &str) -> GraphObserveOne
pub fn observe(&self, path: &str) -> GraphObserveOne
Tap a single node’s downstream message stream. Pure-namespace
resolution; pass &Core to the returned handle’s subscribe.
Sourcepub fn observe_all(&self) -> GraphObserveAll
pub fn observe_all(&self) -> GraphObserveAll
Tap every named node in this graph (snapshot at subscribe()).
Sourcepub fn observe_all_reactive(&self) -> GraphObserveAllReactive
pub fn observe_all_reactive(&self) -> GraphObserveAllReactive
Tap every named node AND auto-subscribe late-added nodes.
Sourcepub fn fire_namespace_change(&self, core: &Core)
pub fn fire_namespace_change(&self, core: &Core)
Fire all namespace-change sinks owner-side (D246 rule 2 —
caller holds &Core).
Sourcepub fn add(
&self,
core: &Core,
node_id: NodeId,
name: impl Into<String>,
) -> Result<NodeId, NameError>
pub fn add( &self, core: &Core, node_id: NodeId, name: impl Into<String>, ) -> Result<NodeId, NameError>
Sourcepub fn try_resolve(&self, path: &str) -> Option<NodeId>
pub fn try_resolve(&self, path: &str) -> Option<NodeId>
Non-panicking Self::node.
Sourcepub fn name_of(&self, node_id: NodeId) -> Option<String>
pub fn name_of(&self, node_id: NodeId) -> Option<String>
Reverse lookup: the local name for a node_id.
Sourcepub fn node_count(&self) -> usize
pub fn node_count(&self) -> usize
Number of named nodes in this graph.
Sourcepub fn node_names(&self) -> Vec<String>
pub fn node_names(&self) -> Vec<String>
Snapshot of local node names in insertion order.
Sourcepub fn child_names(&self) -> Vec<String>
pub fn child_names(&self) -> Vec<String>
Snapshot of mounted child names in insertion order.
Sourcepub fn child(&self, name: &str) -> Option<Graph>
pub fn child(&self, name: &str) -> Option<Graph>
Look up an immediate child mount by name. Returns None when no
child mount with that exact name exists.
/qa G1.1 (2026-05-22): added to support multi-segment-path
navigation in apply_wal_frame mount/unmount arms (graph.ts’s
_collectSubgraphs emits "parent::child::nested" paths; the
Rust storage replay must walk segments to reach the right
owner graph before calling mount_new / unmount). Reverse of
Self::child_names; combine with the segments of a
[PATH_SEP]-joined path to descend the mount tree.
Sourcepub fn is_destroyed(&self) -> bool
pub fn is_destroyed(&self) -> bool
Returns true after Self::destroy has been called.
Sourcepub fn state(
&self,
core: &Core,
name: impl Into<String>,
initial: Option<HandleId>,
) -> Result<NodeId, NameError>
pub fn state( &self, core: &Core, name: impl Into<String>, initial: Option<HandleId>, ) -> Result<NodeId, NameError>
Sourcepub fn derived(
&self,
core: &Core,
name: impl Into<String>,
deps: &[NodeId],
fn_id: FnId,
equals: EqualsMode,
) -> Result<NodeId, NameError>
pub fn derived( &self, core: &Core, name: impl Into<String>, deps: &[NodeId], fn_id: FnId, equals: EqualsMode, ) -> Result<NodeId, NameError>
Sourcepub fn dynamic(
&self,
core: &Core,
name: impl Into<String>,
deps: &[NodeId],
fn_id: FnId,
equals: EqualsMode,
) -> Result<NodeId, NameError>
pub fn dynamic( &self, core: &Core, name: impl Into<String>, deps: &[NodeId], fn_id: FnId, equals: EqualsMode, ) -> Result<NodeId, NameError>
Sourcepub fn set(&self, core: &Core, name: &str, handle: HandleId)
pub fn set(&self, core: &Core, name: &str, handle: HandleId)
Emit a value on a named state node.
Sourcepub fn invalidate_by_name(&self, core: &Core, name: &str)
pub fn invalidate_by_name(&self, core: &Core, name: &str)
Clear the cache of a named node and cascade [INVALIDATE].
Sourcepub fn complete_by_name(&self, core: &Core, name: &str)
pub fn complete_by_name(&self, core: &Core, name: &str)
Mark a named node terminal with COMPLETE.
Sourcepub fn error_by_name(&self, core: &Core, name: &str, error_handle: HandleId)
pub fn error_by_name(&self, core: &Core, name: &str, error_handle: HandleId)
Mark a named node terminal with ERROR.
Sourcepub fn remove(
&self,
core: &Core,
name: &str,
) -> Result<GraphRemoveAudit, RemoveError>
pub fn remove( &self, core: &Core, name: &str, ) -> Result<GraphRemoveAudit, RemoveError>
Remove a named node OR mounted subgraph (R3.2.3 / R3.7.3 ordering — namespace cleared AFTER the TEARDOWN cascade).
§Errors
See RemoveError.
Sourcepub fn edges(&self, core: &Core, recursive: bool) -> Vec<(String, String)>
pub fn edges(&self, core: &Core, recursive: bool) -> Vec<(String, String)>
Derive [from, to] edge name pairs. recursive qualifies names
across the mount tree with ::.
Sourcepub fn subscribe(
&self,
core: &Core,
node_id: NodeId,
sink: Sink,
) -> SubscriptionId
pub fn subscribe( &self, core: &Core, node_id: NodeId, sink: Sink, ) -> SubscriptionId
Subscribe a sink. Returns a SubscriptionId; pass it back to
Self::unsubscribe (owner-invoked, synchronous — D246 rule 3;
no RAII below the binding).
Sourcepub fn unsubscribe(&self, core: &Core, node_id: NodeId, sub_id: SubscriptionId)
pub fn unsubscribe(&self, core: &Core, node_id: NodeId, sub_id: SubscriptionId)
Detach a sink previously registered via Self::subscribe.
Sourcepub fn unsubscribe_topology(&self, core: &Core, id: TopologySubscriptionId)
pub fn unsubscribe_topology(&self, core: &Core, id: TopologySubscriptionId)
Detach a Core topology subscription by id.
Sourcepub fn emit(&self, core: &Core, node_id: NodeId, new_handle: HandleId)
pub fn emit(&self, core: &Core, node_id: NodeId, new_handle: HandleId)
Emit a value on a state node.
Sourcepub fn has_fired_once(&self, core: &Core, node_id: NodeId) -> bool
pub fn has_fired_once(&self, core: &Core, node_id: NodeId) -> bool
Whether the node’s fn has fired at least once.
Sourcepub fn error(&self, core: &Core, node_id: NodeId, error_handle: HandleId)
pub fn error(&self, core: &Core, node_id: NodeId, error_handle: HandleId)
Mark the node terminal with ERROR.
Sourcepub fn invalidate(&self, core: &Core, node_id: NodeId)
pub fn invalidate(&self, core: &Core, node_id: NodeId)
Clear the node’s cache and cascade [INVALIDATE].
Sourcepub fn pause(
&self,
core: &Core,
node_id: NodeId,
lock_id: LockId,
) -> Result<(), PauseError>
pub fn pause( &self, core: &Core, node_id: NodeId, lock_id: LockId, ) -> Result<(), PauseError>
Sourcepub fn resume(
&self,
core: &Core,
node_id: NodeId,
lock_id: LockId,
) -> Result<Option<ResumeReport>, PauseError>
pub fn resume( &self, core: &Core, node_id: NodeId, lock_id: LockId, ) -> Result<Option<ResumeReport>, PauseError>
Sourcepub fn alloc_lock_id(&self, core: &Core) -> LockId
pub fn alloc_lock_id(&self, core: &Core) -> LockId
Allocate a fresh LockId.
Sourcepub fn set_deps(
&self,
core: &Core,
n: NodeId,
new_deps: &[NodeId],
) -> Result<(), SetDepsError>
pub fn set_deps( &self, core: &Core, n: NodeId, new_deps: &[NodeId], ) -> Result<(), SetDepsError>
Atomically rewire a node’s deps.
§Errors
See SetDepsError.
§Hazards
Re-entrant set_deps from inside the firing node’s own fn
corrupts Dynamic tracked indices (D1 in
~/src/graphrefly-rs/docs/porting-deferred.md). Acceptable v1:
most callers are external orchestrators, not the firing node.
Sourcepub fn set_resubscribable(
&self,
core: &Core,
node_id: NodeId,
resubscribable: bool,
)
pub fn set_resubscribable( &self, core: &Core, node_id: NodeId, resubscribable: bool, )
Mark the node as resubscribable (R2.2.7).
Sourcepub fn add_meta_companion(&self, core: &Core, parent: NodeId, companion: NodeId)
pub fn add_meta_companion(&self, core: &Core, parent: NodeId, companion: NodeId)
Attach companion as a meta companion of parent (R1.3.9.d).
Sourcepub fn batch<F: FnOnce()>(&self, core: &Core, f: F)
pub fn batch<F: FnOnce()>(&self, core: &Core, f: F)
Coalesce multiple emissions into a single wave.
Sourcepub fn signal(&self, core: &Core, kind: SignalKind)
pub fn signal(&self, core: &Core, kind: SignalKind)
General broadcast (canonical R3.7.1).
Sourcepub fn signal_invalidate(&self, core: &Core)
pub fn signal_invalidate(&self, core: &Core)
Broadcast [INVALIDATE] across this graph + mount tree
(meta-companion filtered per R3.7.2; Graph locks dropped before
any Core::invalidate). Idempotent on a destroyed graph.
Sourcepub fn destroy(&self, core: &Core)
pub fn destroy(&self, core: &Core)
Tear down every named node + recursively into mounted children, then clear namespace + mount-tree state. R3.7.3 ordering (children-first, then own teardown, then clear, then fire ns-change) preserved verbatim.
Sourcepub fn mount(
&self,
core: &Core,
name: impl Into<String>,
child: &Graph,
) -> Result<Graph, MountError>
pub fn mount( &self, core: &Core, name: impl Into<String>, child: &Graph, ) -> Result<Graph, MountError>
Embed an existing child subgraph under name. Fires ns-change
sinks owner-side (P3) — hence &Core (D246 rule 2).
§Errors
See MountError.
Sourcepub fn mount_with<F: FnOnce(&Graph)>(
&self,
core: &Core,
name: impl Into<String>,
builder: F,
) -> Result<Graph, MountError>
pub fn mount_with<F: FnOnce(&Graph)>( &self, core: &Core, name: impl Into<String>, builder: F, ) -> Result<Graph, MountError>
Sourcepub fn unmount(
&self,
core: &Core,
name: &str,
) -> Result<GraphRemoveAudit, MountError>
pub fn unmount( &self, core: &Core, name: &str, ) -> Result<GraphRemoveAudit, MountError>
Source§impl Graph
impl Graph
Sourcepub fn resource_profile(
&self,
core: &dyn CoreFull,
opts: Option<GraphProfileOptions>,
) -> GraphProfileResult
pub fn resource_profile( &self, core: &dyn CoreFull, opts: Option<GraphProfileOptions>, ) -> GraphProfileResult
Snapshot-based runtime profile per canonical R3.6.3 (D285).
Walks the local namespace + mount tree once via
[describe_of], queries CoreFull::sink_count_of per local
node, computes orphan categorization + hotspot rankings. Pure
read — no Core mutation.
Mirrors the post-D284 narrower GraphProfileResult shape that
drops value-size fields (see [crate::profile] module
docstring).
Source§impl Graph
impl Graph
Sourcepub fn snapshot(&self, core: &Core) -> GraphPersistSnapshot
pub fn snapshot(&self, core: &Core) -> GraphPersistSnapshot
Serialize this graph’s state into a portable snapshot.
§Concurrent-mutation caveat (torn read; M4.E1 / D167)
snapshot() is a point-in-time best-effort capture, not an
isolated read. The implementation holds the graph’s inner lock
for the namespace walk (collect names + child mounts), then
drops it before per-node core.cache_of / core.is_terminal
queries. If another thread (or a re-entrant wave) mutates state
during the post-walk phase, the snapshot may capture a mix of
pre- and post-mutation values for different nodes — individual
node slices are internally consistent, but the cross-node
composition is not transaction-isolated.
The TS impl has the same semantics. No user has requested snapshot-level isolation. If you need a consistent cross-node view, the supported pattern is:
- Wrap the snapshot call in
Core::batch(drains the wave beforesnapshot()returns; subsequent emissions wait). - OR call
graph.signal(SignalKind::Pause(lock))first, thensnapshot(), thenResume(lock)— explicitly freezes the reactive layer for the duration.
A future copy-on-write epoch / snapshot-under-lock would close the torn-read window at the cost of holding the Core lock for the full serialization walk; gated on D196 consumer-pressure (no scenario today justifies the lock-contention trade).
Sourcepub fn snapshot_full(&self, core: &dyn CoreFull) -> GraphPersistSnapshot
pub fn snapshot_full(&self, core: &dyn CoreFull) -> GraphPersistSnapshot
Self::snapshot over the one object-safe facade (D246 rule 5)
— for the storage in-wave MailboxOp::Defer(|cf: &dyn CoreFull|)
path, which only has a &dyn CoreFull (not a concrete &Core).
Read-only; serialize_handle delegates to the binding.
Inherits the same concurrent-mutation caveat as Self::snapshot.
Sourcepub fn restore(
&self,
core: &Core,
snapshot: &GraphPersistSnapshot,
) -> Result<(), SnapshotError>
pub fn restore( &self, core: &Core, snapshot: &GraphPersistSnapshot, ) -> Result<(), SnapshotError>
Restore state from a snapshot into this existing graph.
§Errors
NameMismatch if names differ; UnknownNode/UnknownSubgraph
for snapshot entries absent from the graph.
Sourcepub fn from_snapshot(
core: &Core,
snapshot: &GraphPersistSnapshot,
builder: Option<SnapshotBuilder>,
factories: Option<IndexMap<String, NodeFactory>>,
) -> Result<Self, SnapshotError>
pub fn from_snapshot( core: &Core, snapshot: &GraphPersistSnapshot, builder: Option<SnapshotBuilder>, factories: Option<IndexMap<String, NodeFactory>>, ) -> Result<Self, SnapshotError>
Reconstruct a graph from a snapshot. Builder mode
(builder = Some): build topology then restore() values.
Auto-hydration (builder = None): reconstruct topology +
state from the snapshot via factories (state nodes need none).
D246: the embedder owns the Core (see
graphrefly_core::OwnedCore) and passes it in; the binding is
core.binding_ptr().
§Errors
UnresolvableDeps if auto-hydration can’t resolve a node’s
deps; MissingFactory for a non-state node type with no factory.