hidpp/feature/feature_set.rs
1//! Implements the `FeatureSet` feature (ID `0x0001`) that allows enumerating
2//! all the features supported by a device.
3
4use openlogi_hidpp_derive::Feature;
5
6use crate::{
7 channel::AbandonedReply,
8 feature::{FeatureEndpoint, FeatureType},
9 protocol::v20::Hidpp20Error,
10};
11
12/// Implements the `FeatureSet` / `0x0001` feature.
13///
14/// This feature is primarily used to collect all features supported by the
15/// device. To achieve this, call [`Self::count`] to retrieve the amount of
16/// supported features (excluding the root feature). Then call
17/// [`Self::get_feature`] for every `i in 1..=count` (1-based, as accessing the
18/// root feature is not allowed).
19///
20/// The table is fixed for the life of the connection: no write changes what
21/// either function answers. Both therefore adopt a reply still owed to an
22/// abandoned identical ask ([`AbandonedReply::AdoptIdentical`]), so a walk
23/// that re-asks for an entry the link dropped is not held for the channel's
24/// stale-reply grace on every retry — the one place that trade is sound.
25#[derive(Clone, Feature)]
26#[creatable(id = 0x0001, version = 0)]
27pub struct FeatureSetFeature {
28 /// The endpoint this feature talks to.
29 endpoint: FeatureEndpoint,
30}
31
32impl FeatureSetFeature {
33 /// Retrieves the amount of features supported by the device, not including
34 /// the root feature.
35 pub async fn count(&self) -> Result<u8, Hidpp20Error> {
36 Ok(self
37 .endpoint
38 .call_with(0, [0; 3], AbandonedReply::AdoptIdentical)
39 .await?
40 .extend_payload()[0])
41 }
42
43 /// Retrieves the information about a specific feature based on its index in
44 /// the feature table.
45 ///
46 /// Feature index `0` for the root feature is not allowed.
47 pub async fn get_feature(&self, index: u8) -> Result<FeatureInformation, Hidpp20Error> {
48 let payload = self
49 .endpoint
50 .call_with(1, [index, 0x00, 0x00], AbandonedReply::AdoptIdentical)
51 .await?
52 .extend_payload();
53
54 Ok(FeatureInformation {
55 id: u16::from(payload[0]) << 8 | u16::from(payload[1]),
56 typ: FeatureType::from_bits_retain(payload[2]),
57 version: payload[3],
58 })
59 }
60}
61
62/// Represents information about a specific feature as returned by the
63/// [`FeatureSetFeature::get_feature`] function.
64#[derive(Clone, Copy, Hash, Debug)]
65#[cfg_attr(feature = "serde", derive(serde::Serialize))]
66#[non_exhaustive]
67pub struct FeatureInformation {
68 /// The protocol ID of the feature.
69 pub id: u16,
70
71 /// The type of the feature.
72 pub typ: FeatureType,
73
74 /// The latest supported version of the feature.
75 ///
76 /// Multi-version features are always backwards compatible as long as the
77 /// feature ID does not change, meaning functions implemented for an older
78 /// version of the same feature will behave as expected for every later
79 /// version.
80 ///
81 /// This field was added in feature version 1 and will be `0` for all older
82 /// versions.
83 pub version: u8,
84}