ReportDescriptorBuilder

Struct ReportDescriptorBuilder 

Source
pub struct ReportDescriptorBuilder<S: ReportDescriptorBuilderState> { /* private fields */ }
Expand description

A struct for programatically building a HID Report Descriptor.

use hut::{self, AsUsage};

let mut builder = ReportDescriptorBuilder::new();
let rdesc: Vec<u8> = builder
       .usage_page(hut::UsagePage::GenericDesktop)
       .usage_id(hut::GenericDesktop::Mouse)
       .open_collection(CollectionItem::Application)
       .open_collection(CollectionItem::Physical)
       .push()
       .append(LogicalMinimum::from(0).into())
       .append(LogicalMaximum::from(128).into())
       .pop()
       .append(ReportCount::from(2).into())
       .append(ReportSize::from(8).into())
       .usage_id(hut::GenericDesktop::X)
       .usage_id(hut::GenericDesktop::Y)
       .input(ItemBuilder::new()
              .variable()
              .absolute()
              .input())
       .close_collection()
       .close_collection()
       .build();

Implementations§

Source§

impl<S: ReportDescriptorBuilderState> ReportDescriptorBuilder<S>

Source

pub fn append(self, item: ItemType) -> Self

Append an item to this builder. This will append the necesssary bytes once ReportDescriptorBuilder::build() is called.

Source

pub fn usage_page(self, usage_page: impl AsUsagePage) -> Self

Append the given UsagePage to the report descriptor.

This only appends the Usage Page but not the Usage ID for the given Usage. This may result in subtle bugs where the Usage Page differs from the subsequent Usage.

However, since most report descriptors seen in the wild use the LocalItem::Usage for the Usage ID only, this wrapper is provided to make the code more obvious.

In this buggy example code the actual usage used in the report descriptor will be GenericDesktop Mouse.

This example illustrates a bug
use hidreport::types::*;
use hidreport::{Field, ReportDescriptor, Report, VariableField, Usage};
use hut::{self, AsUsagePage, AsUsage};

let rdesc = ReportDescriptorBuilder::new()
    .usage_page(hut::GenericDesktop::X) // sets Usage Page to GenericDesktop
    .usage_id(hut::Arcade::CoinDoor) // BUG: sets Usage ID to 0x2
    .append(LogicalMinimum::from(0).into())
    .append(LogicalMaximum::from(128).into())
    .append(ReportCount::from(1).into())
    .append(ReportSize::from(8).into())
    .input(ItemBuilder::new()
           .variable()
           .absolute()
          .input())
    .build();

    let rdesc: ReportDescriptor = ReportDescriptor::try_from(&rdesc).unwrap();
    let input = rdesc.input_reports().first().unwrap();
    let item = input.fields().first().unwrap();
    let usage: Usage = match item {
        Field::Variable(VariableField {
            usage, ..
        }) => *usage,
        _ => panic!("Invalid field type"),
    };

    let expected_buggy_usage: Usage = hut::GenericDesktop::Mouse.usage().into();
    assert_eq!(usage, expected_buggy_usage);

This is a convenience wrapper for Self::append().

Source

pub fn usage_id(self, usage: impl AsUsage) -> Self

Append the given Usage ID to the report descriptor.

This only appends the Usage ID but not the Usage Page for the given Usage. This may result in subtle bugs where the Usage Page differs from the Usage.

However, since most report descriptors seen in the wild use the LocalItem::Usage for the Usage ID only, this wrapper is provided to make the code more obvious.

In this buggy example code the actual usage used in the report descriptor will be Button 0x30.

This example illustrates a bug
use hidreport::types::*;
use hut::{self, AsUsagePage, AsUsage};

let rdesc = ReportDescriptorBuilder::new()
    .usage_page(hut::UsagePage::Button) // sets Usage Page to Button
    .usage_id(hut::GenericDesktop::X) // BUG: sets Usage ID to 0x30
    .build();

See Self::usage_page() for an more detailed example on a Usage Page and Usage ID mismatch.

This is a convenience wrapper for Self::append().

Source

pub fn input(self, item: InputItem) -> Self

Append the InputItem. Use the ItemBuilder with ItemBuilder::input() to create this item.

This is a convenience wrapper for Self::append().

Source

pub fn output(self, item: OutputItem) -> Self

Append the OutputItem. Use the ItemBuilder with ItemBuilder::output() to create this item.

This is a convenience wrapper for Self::append().

Source

pub fn feature(self, item: FeatureItem) -> Self

Append the FeatureItem. Use the ItemBuilder with ItemBuilder::feature() to create this item.

This is a convenience wrapper for Self::append().

Source§

impl ReportDescriptorBuilder<ReportDescriptorBuilderToplevel>

Source

pub fn new() -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel>

Create a new builder

Source

pub fn open_collection( self, item: CollectionItem, ) -> ReportDescriptorBuilder<ReportDescriptorBuilderC1>

Open a new collection for the builder. This collection must be closed with a call to close_collection().

This is a convenience function and identical to append() with a CollectionItem. However it enforces that all collections are correctly closed before build() can be called.

Source

pub fn push( self, ) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevelPush>

Pushes the current builder stack (use pop() to return to the current state).

This is a convenience wrapper for append() with a GlobalItem::Push. However it enforces that a Push is followed by a Pop before build() can be called or another collection can be opened.

It is not possible to open_collection() while in the logical Pushed state. This is a restriction by this crate, use append() directly where this is required.

Source

pub fn build(&self) -> Vec<u8>

Build the report descriptor bytes based on the current builder state.

This optimizes each HID Items to be of the minimum required, i.e. a value that fits into a u8 will be encoded as HID Item with a data length of 1, etc.

This does not optimize potentially duplicate fields added by the caller, e.g. adding two consecutive ReportSize() in the builder will result in both of these showing up in the resulting report descriptor byte array.

Source§

impl ReportDescriptorBuilder<ReportDescriptorBuilderToplevelPush>

Source

pub fn pop(self) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel>

Pops the current builder stack, see push().

This is a convenience wrapper for append() with a GlobalItem::Pop. However it enforces that a Push is followed by a Pop before build() can be called or another collection can be opened.

It is not possible to open_collection() while in the logical Pushed state. This is a restriction by this crate, use append() directly where this is required.

Source§

impl ReportDescriptorBuilder<ReportDescriptorBuilderC1>

Source

pub fn close_collection( self, ) -> ReportDescriptorBuilder<ReportDescriptorBuilderToplevel>

Close the current collection.

This function is only available after open_collection().

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, 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, 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.