xmpp-parsers: Make DiscoInfoResult::features a BTreeSet

Features must never be duplicated, so we can use a BTreeSet instead of a
Vec to be nicer for users.

It makes the internal API for computing caps and ecaps2 a bit worse,
because it was expecting a slice directly, so for now let’s collect the
BTreeSet into a Vec when computing the thing.  A refactor to use
Iterator might make it better eventually, but I won’t work on that
before profiling it.
This commit is contained in:
Link Mauve 2026-02-21 20:34:36 +01:00
commit bc88134c41
6 changed files with 20 additions and 9 deletions

View file

@ -13,7 +13,8 @@ XXXX-YY-ZZ RELEASER <admin@example.com>
- Remove the unnecessary SaslMechanisms struct, to directly extract
SASL mechanisms in a Vec<String> in StreamFeatures.
- The type of disco::DiscoInfoResult::features changed from
Vec<Feature> to Vec<String>, this lets us remove Feature as well.
Vec<Feature> to BTreeSet<String>, this lets us remove Feature as
well.
- bind::BindFeature::required is now a bool, thanks to xsos flag.
- jingle_rtp::Description::rtcp_mux is now a bool flag too.
- stream_limits::Limits now extract directly to NonZeroU32.

View file

@ -125,8 +125,11 @@ fn compute_extensions(extensions: &[DataForm]) -> Vec<u8> {
/// [ecaps2](../ecaps2/index.html) instead, see [this
/// email](https://mail.jabber.org/pipermail/security/2009-July/000812.html).
pub fn compute_disco(disco: &DiscoInfoResult) -> Vec<u8> {
// TODO: Figure out a way to remove the clones here.
let features: Vec<_> = disco.features.iter().cloned().collect();
let identities_string = compute_identities(&disco.identities);
let features_string = compute_features(&disco.features);
let features_string = compute_features(&features);
let extensions_string = compute_extensions(&disco.extensions);
let mut final_string = vec![];

View file

@ -10,6 +10,7 @@ use crate::data_forms::DataForm;
use crate::iq::{IqGetPayload, IqResultPayload};
use crate::ns;
use crate::rsm::{SetQuery, SetResult};
use alloc::collections::BTreeSet;
use jid::Jid;
/// Structure representing a `<query xmlns='http://jabber.org/protocol/disco#info'/>` element.
@ -98,7 +99,7 @@ pub struct DiscoInfoResult {
/// List of features supported by this entity.
#[xml(extract(n = .., name = "feature", fields(attribute(name = "var", type_ = String))))]
pub features: Vec<String>,
pub features: BTreeSet<String>,
/// List of extensions reported by this entity.
#[xml(child(n = ..))]

View file

@ -124,7 +124,10 @@ fn compute_extensions(extensions: &[DataForm]) -> Result<Vec<u8>, Error> {
/// XEP-0390](https://xmpp.org/extensions/xep-0390.html#algorithm-input) on a
/// [disco#info query element](../disco/struct.DiscoInfoResult.html).
pub fn compute_disco(disco: &DiscoInfoResult) -> Result<Vec<u8>, Error> {
let features_string = compute_features(&disco.features);
// TODO: Figure out a way to remove the clones here.
let features: Vec<_> = disco.features.iter().cloned().collect();
let features_string = compute_features(&features);
let identities_string = compute_identities(&disco.identities);
let extensions_string = compute_extensions(&disco.extensions)?;