Skip to main content

ToolRouter

Struct ToolRouter 

Source
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

Source

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:

  1. No two applicable servers (in any language) may share a ServerId — it is the key of every map keyed by server identity.
  2. No two applicable servers for one language may both omit handles (two catch-alls).
  3. No tool may be claimed via handles by 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.

Source

pub fn catch_all<I>(entries: I) -> Self
where I: IntoIterator<Item = (ServerId, String)>,

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.

Source

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.

Source

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.

Source

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:

  1. the first server that explicitly claims tool;
  2. 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.

Source

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.

Trait Implementations§

Source§

impl Debug for ToolRouter

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Default for ToolRouter

Source§

fn default() -> ToolRouter

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more