pub struct ToolRouter { /* private fields */ }Expand description
Resolves (language, tool) to the ServerId that should handle it.
Built once at startup by Self::from_configs over the applicable
(post-heuristics) server configs, then rebound once at registration time
by Self::rebind_to_registered so that no route ever points at a
server that failed to spawn.
Implementations§
Source§impl ToolRouter
impl ToolRouter
Sourcepub fn from_configs<'a, I>(cfgs: I) -> Result<Self>where
I: IntoIterator<Item = &'a LspServerConfig>,
pub fn from_configs<'a, I>(cfgs: I) -> Result<Self>where
I: IntoIterator<Item = &'a LspServerConfig>,
Build a router from the configs applicable in this workspace, enforcing the workspace-scoped validation rules:
- No two applicable servers (in any language) may share a
ServerId— it is the key of every map keyed by server identity. - No two applicable servers for one language may both omit
handles(two catch-alls). - No tool may be claimed via
handlesby two applicable servers of the same language.
Also emits a tracing::warn! for any language whose union of
handles claims is partial and has no catch-all server, naming the
tools nobody will serve.
§Errors
Returns Error::InvalidConfig naming the conflicting entries if any
of the three rules above is violated.
Sourcepub fn catch_all<I>(entries: I) -> Self
pub fn catch_all<I>(entries: I) -> Self
Build a router where every entry is a catch-all for its language.
Test helper: takes (id, language) pairs rather than a single entry
because some tests (e.g. the typescript/typescriptreact exact-match
preference) need two catch-alls registered at once.
Sourcepub fn rebind_to_registered(&mut self, registered: &HashSet<ServerId>)
pub fn rebind_to_registered(&mut self, registered: &HashSet<ServerId>)
Rebind every route pointing at a server that did not register — i.e. failed to spawn — to that language’s live catch-all, or drop the route entirely if no catch-all is live.
A dead route is never rebound to a narrowly-scoped live server: a
server that declared handles = [...] has explicitly declined every
other tool, and conscripting it would override that declaration (and,
via the diagnostics cache filter, start caching diagnostics the user
deliberately routed away).
§Preconditions
Call this exactly once, after all spawn attempts for a serve_with
invocation have completed and before any request can observe the
router. This is sound only because LspServer::spawn_batch is a
sequential loop that produces one ServerInitResult registered under
a single lock — registration is one atomic all-or-nothing event, so
no request can observe a half-rebound router. If server registration
is ever made incremental (servers registering as they finish spawning,
rather than all together), an early rebind here would permanently
steal a slow server’s routes with no way back; this function would
need to be replaced with a design that derives the active table on
each lookup instead of mutating it once.
Sourcepub fn resolve(&self, language_id: &str, tool: ToolKind) -> Option<&ServerId>
pub fn resolve(&self, language_id: &str, tool: ToolKind) -> Option<&ServerId>
Resolve the server that should handle tool for language_id.
Explicit claims win over the language’s catch-all; if neither exists,
returns None.
Sourcepub fn resolve_any(&self, tool: ToolKind) -> Result<&ServerId, NoServerReason>
pub fn resolve_any(&self, tool: ToolKind) -> Result<&ServerId, NoServerReason>
Resolve a server for tool without a specific language — used for
workspace-wide tools like workspace_symbol_search that have no
document to detect a language from.
Resolves in two tiers, in config declaration order:
- the first server that explicitly claims
tool; - else the first catch-all server.
Deliberately does not fall back to “the first server at all” when
neither tier matches: a server with a handles list has explicitly
declined every tool not on it, so forwarding an unclaimed workspace-wide
tool to it anyway would silently violate that declaration. Callers get
NoServerReason instead, distinguishing “nothing configured” from
“something is configured but nothing claims this tool” so they can
report a precise error rather than defaulting to an arbitrary server.
§Errors
Returns NoServerReason::NothingRegistered if no server is
registered at all, or NoServerReason::NoClaimant if servers are
registered but none explicitly claims tool and none is a catch-all.
Sourcepub fn has_language(&self, language_id: &str) -> bool
pub fn has_language(&self, language_id: &str) -> bool
Whether language_id currently has at least one live-or-configured
route (a catch-all or an explicit claim), used to distinguish
NoServerForTool (some server handles this language, just not this
tool) from NoServerForLanguage (nothing does).
Deliberately checks route contents, not just map-key presence: after
rebind_to_registered drops every route for a language whose sole
server failed to spawn, this must go back to false so that language
reports NoServerForLanguage exactly as it did before per-tool
routing existed, not NoServerForTool.