Skip to main content

rmk_types/
lib.rs

1//! # RMK Types
2//!
3//! Shared type definitions used across the RMK keyboard firmware ecosystem.
4//!
5//! ## Modules
6//!
7//! ### Actions & keycodes
8//! - [`action`] — What keys do: `Action`, `KeyAction`, `EncoderAction`, `LightAction`, etc.
9//! - [`keycode`] — What keys are: `KeyCode`, `HidKeyCode`, `ConsumerKey`, `SystemControlKey`
10//!
11//! ### Behaviors (key overrides, combos, tap-dance)
12//! - [`combo`] — `Combo`: combo trigger configuration
13//! - [`fork`] — `Fork`, `StateBits`: key-override configuration
14//! - [`morse`] — `Morse`, `MorsePattern`, `MorseProfile`, `MorseMode`: tap-dance/tap-hold
15//!
16//! ### Hardware state
17//! - [`modifier`] — `ModifierCombination` bitfield
18//! - [`mouse_button`] — `MouseButtons` bitfield
19//! - [`led_indicator`] — `LedIndicator` bitfield
20//! - [`battery`] — `BatteryStatus`, `ChargeState`
21//! - [`ble`] — `BleStatus`, `BleState`
22//! - [`connection`] — `ConnectionType` (USB/BLE), `UsbState`, `ConnectionStatus`
23//!
24//! ### Protocol
25//! - [`protocol::vial`] — Vial/Via protocol types
26//! - [`protocol::rynk`] — RMK native protocol ICD (feature-gated: `rynk`)
27//!
28//! ### Build-time
29//! - [`constants`] — Generated from `keyboard.toml` by `build.rs`
30
31#![cfg_attr(not(feature = "wasm"), no_std)]
32
33// The host build (no_std, but on an allocator-backed platform) uses `alloc::Vec`
34// for bulk message fields, which are unbounded there — see `protocol::rynk`.
35#[cfg(feature = "host")]
36extern crate alloc;
37
38pub mod action;
39pub mod battery;
40pub mod ble;
41pub mod combo;
42pub mod connection;
43pub mod constants;
44#[cfg(feature = "dfu")]
45pub mod dfu;
46pub mod fmt;
47pub mod fork;
48pub mod keycode;
49pub mod led_indicator;
50pub mod modifier;
51pub mod morse;
52pub mod mouse_button;
53pub mod protocol;
54#[cfg(feature = "steno")]
55pub mod steno;
56
57/// Compute the maximum varint-encoded length for a given max value.
58/// Mirrors `postcard`'s internal `varint_size`.
59pub(crate) const fn varint_max_size(max_n: usize) -> usize {
60    const BITS_PER_BYTE: usize = 8;
61    const BITS_PER_VARINT_BYTE: usize = 7;
62    if max_n == 0 {
63        return 1;
64    }
65    let bits = core::mem::size_of::<usize>() * BITS_PER_BYTE - max_n.leading_zeros() as usize;
66    let roundup_bits = bits + (BITS_PER_VARINT_BYTE - 1);
67    roundup_bits / BITS_PER_VARINT_BYTE
68}
69
70/// Worst-case postcard-encoded size of `heapless::Vec<T, N>`:
71/// every element at its own max, plus the widest varint for the length prefix.
72///
73/// Use this in manual `MaxSize` impls for structs whose fields contain
74/// `heapless::Vec<T, N>`, since `#[derive(MaxSize)]` doesn't support `heapless::Vec`.
75/// TODO: Use derived `MaxSize` after postcard updates its heapless version.
76pub(crate) const fn heapless_vec_max_size<T: postcard::experimental::max_size::MaxSize, const N: usize>() -> usize {
77    T::POSTCARD_MAX_SIZE * N + varint_max_size(N)
78}
79
80#[cfg(test)]
81mod tests {
82    use heapless::Vec;
83
84    use super::{heapless_vec_max_size, varint_max_size};
85
86    /// Validate varint_max_size against known postcard varint encoding sizes
87    /// and cross-check with actual postcard serialization.
88    #[test]
89    fn varint_max_size_matches_postcard() {
90        // Known varint size boundaries
91        assert_eq!(varint_max_size(0), 1);
92        assert_eq!(varint_max_size(1), 1);
93        assert_eq!(varint_max_size(127), 1);
94        assert_eq!(varint_max_size(128), 2);
95        assert_eq!(varint_max_size(16383), 2);
96        assert_eq!(varint_max_size(16384), 3);
97
98        // Cross-check: serialize actual values with postcard and verify
99        // the varint prefix length doesn't exceed our calculation
100        let mut buf = [0u8; 16];
101        for &n in &[0usize, 1, 127, 128, 255, 256, 16383, 16384, 65535] {
102            let bytes = postcard::to_slice(&n, &mut buf).unwrap();
103            assert!(
104                bytes.len() <= varint_max_size(n),
105                "varint_max_size({n}) = {} but postcard used {} bytes",
106                varint_max_size(n),
107                bytes.len()
108            );
109        }
110    }
111
112    /// Worst-case `Vec<u32, 8>` (every element at `u32::MAX`, max-width varint
113    /// length prefix) must encode to exactly `heapless_vec_max_size::<u32, 8>()`.
114    #[test]
115    fn heapless_vec_max_size_matches_postcard() {
116        let mut v: Vec<u32, 8> = Vec::new();
117        for _ in 0..8 {
118            v.push(u32::MAX).unwrap();
119        }
120        let mut buf = [0u8; 64];
121        let bytes = postcard::to_slice(&v, &mut buf).unwrap();
122        assert_eq!(
123            bytes.len(),
124            heapless_vec_max_size::<u32, 8>(),
125            "tight bound: 8 × u32::MAX + varint(8)",
126        );
127
128        // Empty Vec is below the bound (loose check).
129        let empty: Vec<u32, 8> = Vec::new();
130        let bytes = postcard::to_slice(&empty, &mut buf).unwrap();
131        assert!(bytes.len() <= heapless_vec_max_size::<u32, 8>());
132    }
133}