pub struct Workspace { /* private fields */ }Expand description
A named set of per-repo graphs, each opened on demand and cached. Cheap to
hold: the stores are small SQLite files opened lazily; the caller (a server)
holds the one expensive model. The registry is reloadable in place.
Implementations§
Source§impl Workspace
impl Workspace
Sourcepub fn single(name: impl Into<String>, store: Store) -> Self
pub fn single(name: impl Into<String>, store: Store) -> Self
A single-project workspace over an already-open store, named name.
This is the single-repo serve default and the test constructor; a bare
(no-project) call resolves to it. Not reloadable (no repo paths).
Sourcepub fn from_stores<I, S>(stores: I) -> Self
pub fn from_stores<I, S>(stores: I) -> Self
A workspace over several already-open stores, one per named project — the
in-memory counterpart of Workspace::from_repo_paths (which opens each
project’s graph.db from disk lazily). Used for multi-repo serving of
pre-built stores and for tests. With exactly one project it becomes the
default (as Workspace::single); with several, a bare (no-project)
call is ambiguous. Not reloadable (no repo paths).
Sourcepub fn from_repo_paths<I, P>(paths: I) -> Result<Self, WorkspaceError>
pub fn from_repo_paths<I, P>(paths: I) -> Result<Self, WorkspaceError>
Build a workspace from repo directories: each is git-discovered, named
after its working-tree directory (collisions get a -2, -3, … suffix),
and its graph.db opened lazily. With exactly one repo, that repo is the
default project.
§Errors
WorkspaceError::Git if a path is not inside a git repository, or
WorkspaceError::Empty if paths resolves to no repos.
Sourcepub fn from_named_dbs<I>(dbs: I) -> Self
pub fn from_named_dbs<I>(dbs: I) -> Self
Build a workspace from explicit (project name, graph.db path) pairs,
without git discovery — used where the names and store locations are
already known (WorkspaceSet construction re-uses the CLI’s discovery
upstream, and tests build synthetic registries). Names are taken verbatim
(deduplicate before calling if a collision is possible); with exactly one
pair, that project is the default.
Sourcepub fn member_dbs(&self) -> Vec<PathBuf>
pub fn member_dbs(&self) -> Vec<PathBuf>
The graph.db paths of the workspace’s lazily-opened (Path) projects, in
stable name order. Pre-opened (single) projects carry no path and are
omitted. Used by WorkspaceSet::containing to find which workspace holds
a given repo.
Sourcepub fn project_root(
&self,
project: Option<&str>,
) -> Result<Option<PathBuf>, WorkspaceError>
pub fn project_root( &self, project: Option<&str>, ) -> Result<Option<PathBuf>, WorkspaceError>
The working-tree root of project’s repository, resolving project
the same way Workspace::with_store does (so None means the default
project).
This exists so a caller can read that repository’s own configuration
rather than the invoking process’s. The rule, following ADR-0009’s
per-repo [[links]] resolution: a repository’s own config governs how
it is scanned, whoever is asking. Without it, a server started in repo
A answers questions about repo B using A’s settings — and B’s own
[debt] ignore never applies, so the API and B’s CLI disagree about B.
Returns Ok(None) when the project’s store was handed over pre-opened
(Workspace::single / Workspace::from_stores) or registered by
graph.db path alone (Workspace::from_named_dbs): there is no
repository on disk to consult, and the caller falls back to its own
configuration.
§Errors
WorkspaceError::UnknownProject / WorkspaceError::AmbiguousProject
as Workspace::resolve, or WorkspaceError::Poisoned.
Sourcepub fn with_on_open(
self,
hook: Arc<dyn Fn(&Path) -> Result<(), String> + Send + Sync>,
) -> Self
pub fn with_on_open( self, hook: Arc<dyn Fn(&Path) -> Result<(), String> + Send + Sync>, ) -> Self
Set a first-open hook (serve --sync-on-access): before a project’s store
is opened for the first time, hook is run against its graph.db path to
(re)build it. Applies to lazily-opened Path projects; a pre-opened
single store is already loaded, so the hook does not fire for it.
Sourcepub fn reload_from<I, P>(&self, paths: I) -> Result<Vec<String>, WorkspaceError>
pub fn reload_from<I, P>(&self, paths: I) -> Result<Vec<String>, WorkspaceError>
Rebuild the registry from a fresh set of repo paths: added repos become
available, removed ones are dropped (and their cached store evicted), and
still-present ones keep their warm connection. Returns the new project
names. Use this to reload a running server (e.g. on SIGHUP) without a
restart. A single-project pre-opened workspace (Workspace::single) has
no repo paths, so reloading it simply replaces it with the given repos.
§Errors
Sourcepub fn is_multi(&self) -> bool
pub fn is_multi(&self) -> bool
Whether the workspace holds more than one project (so project selection
is meaningful to expose to callers/tools).
Sourcepub fn resolve(&self, project: Option<&str>) -> Result<String, WorkspaceError>
pub fn resolve(&self, project: Option<&str>) -> Result<String, WorkspaceError>
Resolve project (or the default) to a concrete project name.
§Errors
WorkspaceError::UnknownProject if named but absent,
WorkspaceError::AmbiguousProject if omitted with several projects, or
WorkspaceError::Empty if there are none.
Sourcepub fn with_store<R>(
&self,
project: Option<&str>,
f: impl FnOnce(&Store) -> R,
) -> Result<R, WorkspaceError>
pub fn with_store<R>( &self, project: Option<&str>, f: impl FnOnce(&Store) -> R, ) -> Result<R, WorkspaceError>
Run f with the resolved project’s store (opened and cached on first
use). The store lock is held only for f, never across an .await.
§Errors
As Workspace::resolve, plus WorkspaceError::NoGraph if the store
file is absent, WorkspaceError::Store on open failure, or
WorkspaceError::Poisoned if a lock was poisoned.
Sourcepub fn with_store_mut<R>(
&self,
project: Option<&str>,
f: impl FnOnce(&mut Store) -> R,
) -> Result<R, WorkspaceError>
pub fn with_store_mut<R>( &self, project: Option<&str>, f: impl FnOnce(&mut Store) -> R, ) -> Result<R, WorkspaceError>
Like Workspace::with_store, but hands f a mutable store so it can
persist into the graph (e.g. Store::apply_import_layer). The store lock
is held only for f, never across an .await. Backs the explorer’s
links/write endpoint, which materialises the inferred cross-repo links into
a spoke’s graph as a durable import layer.
§Errors
Sourcepub fn resolve_qualified(
&self,
qualified: &str,
) -> Result<Option<Node>, WorkspaceError>
pub fn resolve_qualified( &self, qualified: &str, ) -> Result<Option<Node>, WorkspaceError>
Resolve a project-qualified key "<project>::<key>" to its node across
the workspace, opening the target project on demand (ADR-0009). Ok(None)
means the key is well-formed and the project exists but the node does not —
i.e. cross-repo drift (a removed or renamed target). Errors distinguish
the other failure modes so a caller can report them precisely:
WorkspaceError::Unqualified (not in <project>::<key> form),
WorkspaceError::UnknownProject (target repo not in the workspace),
WorkspaceError::NoGraph (target repo unsynced).
§Errors
As above, plus WorkspaceError::Store / WorkspaceError::Poisoned.
Sourcepub fn follow_external_ref(
&self,
node: &Node,
) -> Result<Option<Node>, WorkspaceError>
pub fn follow_external_ref( &self, node: &Node, ) -> Result<Option<Node>, WorkspaceError>
Follow an external-ref placeholder node to the real node it stands for,
resolving its project-qualified target across the workspace (ADR-0009). An
external-ref lives in a spoke’s store as a local stand-in for a node in the
hub’s store (see crate::external_ref_node); this walks it through to the
hub. Ok(None) means either node is not an external-ref, or its target no
longer resolves — cross-repo drift (a removed or renamed hub key). Errors
distinguish the other failure modes, as Workspace::resolve_qualified.
§Errors
Sourcepub fn follow_definition(
&self,
qualified: &str,
) -> Result<Follow, WorkspaceError>
pub fn follow_definition( &self, qualified: &str, ) -> Result<Follow, WorkspaceError>
Follow a project-qualified cross-repo target to the most specific definition it names — the follow-the-link hop that turns a click on a spoke’s app-key target into a jump to the hub node that defines it.
Workspace::resolve_qualified lands on the raw hub node a spoke points
at, which for a config override is the hub’s config_key node (e.g.
cfgkey:config.toml#serve.addr), not the Rust struct that declares the
setting. This method adds the net-new config_key → struct bridge: when
the resolved node is a config key whose dotted path maps — with confidence —
to exactly one hub struct and one of its named fields, it returns that
struct as the jump target (Follow::StructField, carrying the matched
field name). Otherwise it returns the resolved node unchanged
(Follow::Node) — a config key we could not bridge, or any non-config
target (e.g. an authored [[links]] that already points at a symbol). A
well-formed target whose node is gone is Follow::Drift.
The bridge is deliberately conservative (see [bridge_config_key]): it
fires only on a unique match of both an independent section→struct-name
signal and a field-presence signal, so it never jumps to a wrong node —
an ambiguous or unmatched key falls back to the config-key node.
§Errors
As Workspace::resolve_qualified (a well-formed but unhosted / unsynced
target project still errors; a resolved-but-missing node is Drift).