hid-descriptor 0.2.0

Tools for building USB HID report descriptors
Documentation
use hid_types::encoding::{TagTypeSize, TypeBits, encode_signed, encode_unsigned};
use hid_types::id::{GlobalItem, IntoTagType, LocalItem, MainItem};
use hid_types::item::{Global, Item, Local, LongItem, Main};

use crate::DescriptorBytes;

/// A trait for items that can be encoded into the HID report descriptor binary format.
pub trait EncodeItem {
    /// Encode the item into a sequence of bytes.
    fn encode(&self) -> DescriptorBytes;
}

impl EncodeItem for LongItem {
    fn encode(&self) -> DescriptorBytes {
        long_encode(self.tag, &self.data)
    }
}

impl EncodeItem for Main {
    fn encode(&self) -> DescriptorBytes {
        match self {
            Main::Input(input_flags) => {
                let data: u32 = input_flags.0.into();
                short_encode_unsigned(MainItem::Input, &data.to_le_bytes())
            }
            Main::Output(output_flags) => {
                let data: u32 = output_flags.0.into();
                short_encode_unsigned(MainItem::Output, &data.to_le_bytes())
            }
            Main::Feature(feature_flags) => {
                let data: u32 = feature_flags.0.into();
                short_encode_unsigned(MainItem::Feature, &data.to_le_bytes())
            }
            Main::Collection(ty) => {
                let data: u8 = ty.to_integer();
                short_encode_unsigned(MainItem::Collection, &data.to_le_bytes())
            }
            Main::EndCollection => short_encode_unsigned(MainItem::EndCollection, &[]),
            Main::Reserved(tag, data) => reserved_encode(*tag, TypeBits::Main, *data),
        }
    }
}

impl EncodeItem for Global {
    fn encode(&self) -> DescriptorBytes {
        match self {
            Global::UsagePage(usage_page) => {
                let data = usage_page.to_integer().to_le_bytes();
                short_encode_unsigned(GlobalItem::UsagePage, &data)
            }
            Global::LogicalMinimum(value) => {
                short_encode_signed(GlobalItem::LogicalMinimum, &value.to_le_bytes())
            }
            Global::LogicalMaximum(value) => {
                short_encode_signed(GlobalItem::LogicalMaximum, &value.to_le_bytes())
            }
            Global::PhysicalMinimum(value) => {
                short_encode_signed(GlobalItem::PhysicalMinimum, &value.to_le_bytes())
            }
            Global::PhysicalMaximum(value) => {
                short_encode_signed(GlobalItem::PhysicalMaximum, &value.to_le_bytes())
            }
            Global::UnitExponent(value) => {
                short_encode_signed(GlobalItem::UnitExponent, &value.to_le_bytes())
            }
            Global::Unit(unit) => {
                let data = unit.to_integer().to_le_bytes();
                short_encode_unsigned(GlobalItem::Unit, &data)
            }
            Global::ReportSize(value) => {
                short_encode_unsigned(GlobalItem::ReportSize, &value.to_le_bytes())
            }
            Global::ReportId(value) => {
                short_encode_unsigned(GlobalItem::ReportId, &value.to_le_bytes())
            }
            Global::ReportCount(value) => {
                short_encode_unsigned(GlobalItem::ReportCount, &value.to_le_bytes())
            }
            Global::Push => short_encode_unsigned(GlobalItem::Push, &[]),
            Global::Pop => short_encode_unsigned(GlobalItem::Pop, &[]),
            Global::Reserved(tag, data) => reserved_encode(*tag, TypeBits::Global, *data),
        }
    }
}

impl EncodeItem for Local {
    fn encode(&self) -> DescriptorBytes {
        match self {
            Local::Usage(usage) => {
                let data = usage.to_integer().to_le_bytes();
                short_encode_unsigned(LocalItem::Usage, &data)
            }
            Local::ExtendedUsage(extended_usage) => {
                let data = extended_usage.to_integer().to_le_bytes();
                short_encode_unsigned(LocalItem::Usage, &data)
            }
            Local::UsageMinimum(value) => {
                short_encode_unsigned(LocalItem::UsageMinimum, &value.to_le_bytes())
            }
            Local::UsageMaximum(value) => {
                short_encode_unsigned(LocalItem::UsageMaximum, &value.to_le_bytes())
            }
            Local::DesignatorIndex(value) => {
                short_encode_unsigned(LocalItem::DesignatorIndex, &value.to_le_bytes())
            }
            Local::DesignatorMinimum(value) => {
                short_encode_unsigned(LocalItem::DesignatorMinimum, &value.to_le_bytes())
            }
            Local::DesignatorMaximum(value) => {
                short_encode_unsigned(LocalItem::DesignatorMaximum, &value.to_le_bytes())
            }
            Local::StringIndex(value) => {
                short_encode_unsigned(LocalItem::StringIndex, &value.to_le_bytes())
            }
            Local::StringMinimum(value) => {
                short_encode_unsigned(LocalItem::StringMinimum, &value.to_le_bytes())
            }
            Local::StringMaximum(value) => {
                short_encode_unsigned(LocalItem::StringMaximum, &value.to_le_bytes())
            }
            Local::Delimiter(value) => {
                let data = u8::from(*value);
                short_encode_unsigned(LocalItem::Delimiter, &data.to_le_bytes())
            }
            Local::Reserved(tag, data) => reserved_encode(*tag, TypeBits::Local, *data),
        }
    }
}

impl EncodeItem for Item {
    fn encode(&self) -> DescriptorBytes {
        match self {
            Item::Long(long_item) => long_item.encode(),
            Item::Main(main) => main.encode(),
            Item::Global(global) => global.encode(),
            Item::Local(local) => local.encode(),
            Item::Reserved(tag, data) => reserved_encode(*tag, TypeBits::Reserved, *data),
        }
    }
}

fn long_encode(tag: u8, data: &[u8]) -> DescriptorBytes {
    // bSize=2, bType=Reserved, bTag=0b1111. This is hardcoded in the HID spec.
    const LONG_TAG_BYTE: u8 = 0xFE;

    let data_size: u8 = data
        .len()
        .try_into()
        .expect("long encoding can only store 255 bytes");

    [LONG_TAG_BYTE, data_size, tag]
        .into_iter()
        .chain(data.iter().copied())
        .collect()
}

fn reserved_encode(tag: u8, ty: TypeBits, data: Option<i32>) -> DescriptorBytes {
    let tts = TagTypeSize::new().with_tag(tag).with_ty(ty);

    let data: &[u8] = match data {
        None => &[],
        Some(bytes) => &bytes.to_le_bytes(),
    };

    short_encode_signed(tts, data)
}

fn short_encode_unsigned<T: IntoTagType>(tag: T, data: &[u8]) -> DescriptorBytes {
    let data = encode_unsigned(data);
    let tag = TagTypeSize::from_tag(tag, data.len());

    [tag.into_bits()]
        .into_iter()
        .chain(data.iter().copied())
        .collect()
}

fn short_encode_signed<T: IntoTagType>(tag: T, data: &[u8]) -> DescriptorBytes {
    let data = encode_signed(data);
    let tag = TagTypeSize::from_tag(tag, data.len());

    [tag.into_bits()]
        .into_iter()
        .chain(data.iter().copied())
        .collect()
}