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. The table in use is derived from that immutable original by Self::rebind each time a server settles, so no route ever points at a server known to have 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 crate::error::Error::Config 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, LanguageId)>,

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(&mut self, settlement: impl Fn(&ServerId) -> ServerSettlement)

Derives the active routes from the configured ones and the current ServerSettlement of every server; a pure function of those, so the result never depends on the order in which servers settled, and once none is Pending it equals what a single batch rebind produces.

Per language:

  • an explicit route to a Registered or Pending server is kept;
  • an explicit route to a Failed server is rebound to the language’s catch-all when that is Registered, kept pointing at the failed server while the catch-all is Pending (the route is never bound to a server that has not registered), and dropped when the catch-all is Failed or absent;
  • a Failed catch-all is dropped;
  • 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).

The server order used by Self::resolve_any keeps every server that is not Failed, so a pending server’s position is preserved.

Silent: callers log the server that just settled, not every earlier failure again.

Source

pub fn rebind_to_registered(&mut self, registered: &HashSet<ServerId>)

Self::rebind for a finished startup: every server in registered is Registered, every other one Failed.

Source

pub fn languages_routed_to(&self, id: &ServerId) -> Vec<LanguageId>

The configured languages with a route (explicit or catch-all) naming id, sorted.

Source

pub fn catch_all_for(&self, language_id: &str) -> Option<&ServerId>

The catch-all server configured for language_id, if any.

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.

Source

pub fn configured_languages(&self) -> Vec<LanguageId>

Every language this router was built with, sorted, including languages whose routes Self::rebind_to_registered has since emptied – so a language whose servers all failed to spawn is still listed (and can be reported as having no server) rather than vanishing.

§Examples
use mcpls_core::config::{LanguageId, ServerId, ToolRouter};

let router = ToolRouter::catch_all([
    (ServerId::from_static("pyright"), LanguageId::from_static("python")),
    (ServerId::from_static("rust-analyzer"), LanguageId::from_static("rust")),
]);
assert_eq!(router.configured_languages(), ["python", "rust"]);

Trait Implementations§

Source§

impl Clone for ToolRouter

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
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() -> Self

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> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<A, B, T> HttpServerConnExec<A, B> for T
where B: Body,

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> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

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

Source§

type Error = !

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

fn try_from(value: U) -> Result<T, !>

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