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}