pub struct FeatureProfile { /* private fields */ }Expand description
The set of features a negotiated session has.
A u128 bitset, so allows is a mask and a test — the
check every feature-specific code path makes before it runs, and one that
must not cost a linear search on the message path.
Implementations§
Source§impl FeatureProfile
impl FeatureProfile
Sourcepub fn all() -> Self
pub fn all() -> Self
Every feature, ignoring revision, capabilities and transport.
Only for tests that need a profile with a known shape. Production code
goes through resolve.
Sourcepub fn iter(self) -> impl Iterator<Item = Feature>
pub fn iter(self) -> impl Iterator<Item = Feature>
The features in the profile, in declaration order.
Sourcepub fn missing(self) -> impl Iterator<Item = Feature>
pub fn missing(self) -> impl Iterator<Item = Feature>
The features that are not in the profile, in declaration order.
Sourcepub const fn difference(self, other: Self) -> Self
pub const fn difference(self, other: Self) -> Self
The features this session has that other does not.
Sourcepub const fn intersection(self, other: Self) -> Self
pub const fn intersection(self, other: Self) -> Self
The features both sessions have.
Sourcepub const fn as_set(self) -> FeatureSet
pub const fn as_set(self) -> FeatureSet
This profile viewed as a FeatureSet.
The two types share one bit encoding precisely so that this is a copy of
a u128 and every comparison between them is a mask test. The
alternative — FeatureSet::union_of(self.iter()) — walks all
Feature::ALL and calls allows for each, and
allows resolves its bit by scanning ALL for a matching name. That is
quadratic in the feature count, which is invisible when a decision is
made once per message and not when it is made once per primitive on a
tools/list of a thousand tools.
Sourcepub fn advertised_by_server(self) -> CapabilitySet
pub fn advertised_by_server(self) -> CapabilitySet
The server-side capabilities this session is on, as a set.
This is the answer to “what does this server advertise”, read back out
of the same profile every feature gate consults. It is a fold over
FEATURES — the same table resolve is a
fold over — taking the server_cap of every server-side row the profile
allows.
§Why a server’s advertisement is derived and not declared
A server’s InitializeResult has to state its capabilities, and the
obvious implementation is a second list beside the feature matrix. That
list would be a second thing to update, and the two would disagree the
first time a feature was added to FEATURES and not to the list: the
gate would switch on a capability the handshake never claimed, and the
peer would have been told at connect time not to use it.
Deriving it inverts that. The advertisement is a function of the gate, so it cannot be edited independently of it, and the only way to widen what a server advertises is to widen what it actually does.
Two properties fall out of reading the profile rather than the declaration:
- Client capabilities are excluded by construction. Their rows are
Side::Clientwith noserver_cap, so there is no path by whichroots,samplingorelicitationreaches aServerCapabilitiesobject, which has no field for them. - The revision narrows it. A capability survives only if the feature behind it is defined in the negotiated version, so a server that declared everything still advertises only what the agreed revision can name.
§Examples
use protocol_droid::domain::{
Capability, CapabilitySet, Feature, Negotiation, ProtocolVersion,
TransportKind, resolve,
};
// A server that declared nothing advertises nothing.
let version = ProtocolVersion::V2025_06_18;
let silent = resolve(Negotiation {
version,
client_caps: CapabilitySet::all_for(version),
server_caps: CapabilitySet::empty(),
transport: TransportKind::Stdio,
});
assert!(silent.advertised_by_server().is_empty());
// A server that declared tools advertises tools, and nothing a
// client-side-only gate would have switched on.
let loud = resolve(Negotiation {
version,
client_caps: CapabilitySet::all_for(version),
server_caps: CapabilitySet::empty().with(Capability::Tools),
transport: TransportKind::Stdio,
});
let advertised = loud.advertised_by_server();
assert!(advertised.contains(Capability::Tools));
assert!(!advertised.contains(Capability::ToolsListChanged));
assert!(!advertised.contains(Capability::Sampling), "client-side");