telepath-macros
Proc-macro crate providing the #[command] attribute that registers a plain Rust
function as a Telepath RPC command — no hand-written dispatch boilerplate required.
This crate is a build-time only dependency of telepath-server. It does not
appear in device firmware at runtime.
Usage
use command;
// Plain command — all args are wire args
// Resource injection — peripheral state passed from ResourceRegistry, not over the wire
The #[command] attribute is re-exported from telepath-server; no direct
dependency on telepath-macros is required in downstream crates.
Generated items
For each #[command] fn foo(…) -> R the macro emits:
| Item | Description |
|---|---|
fn __telepath_shim_foo(input: &[u8], output: &mut [u8]) -> Result<usize, DispatchError> |
Type-erased shim: deserializes args via postcard, calls foo, serializes return value |
fn __telepath_args_schema_foo(out: &mut [u8]) -> Result<usize, ()> |
Writes postcard-encoded NamedType schema for the argument tuple |
fn __telepath_ret_schema_foo(out: &mut [u8]) -> Result<usize, ()> |
Writes postcard-encoded NamedType schema for the return type |
pub const __TELEPATH_CMD_FOO: CommandMetadata |
Const holding name, cmd_id, and function pointers |
static __TELEPATH_REG_FOO: CommandMetadata |
#[linkme::distributed_slice] registration — zero-cost link-time registration in TELEPATH_COMMANDS |
The original function body is preserved unchanged and remains directly callable.
Signature contract
Allowed:
- Free functions only (no
selfreceiver) - Any number of positional arguments with simple identifier patterns
- Wire argument types: any
T: Serialize + DeserializeOwned + postcard_schema::Schema(owned, no references) #[resource]-annotated arguments:&Tor&mut TwhereT: 'static— injected from the server'sResourceRegistry, not deserialized from the wire- Wire and resource arguments may appear in any order
- Return type: any
T: Serialize + postcard_schema::Schema(owned, no references);()means "no payload"
Rejected at compile time:
async fn,unsafe fn- Generic parameters or
whereclauses &T/&mut Targument without the#[resource]attribute&T/&mut Treturn type- Methods (
fn foo(&self, …)) - Non-identifier argument patterns (e.g. tuple destructuring
(a, b): (i32, i32)) - Duplicate
#[resource]types (each resource type may appear at most once)
Wire encoding
- Args: only non-
#[resource]arguments are serialized; resource arguments are server-side only. Serialized as a postcard tuple —()(zero wire args),(T,)(one wire arg),(T1, T2, …)(N wire args). - Return value: serialized standalone (no wrapper tuple)
cmd_id: derived deterministically from(name, args_type_str, ret_type_str)using wire args only via FNV-1a 16-bit. Adding or removing a#[resource]argument does not change the wirecmd_id. Renaming a function or changing a wire argument type is a breaking wire change.- Reserved
cmd_id = 0x0000is avoided by deterministic salt rehashing inderive_cmd_id; the discovery ID is never emitted by user commands
Build
# Built automatically as part of the workspace
# Inspect macro expansion in a consumer crate (requires cargo-expand)
&&
telepath-macros is a workspace member targeting the native host (proc-macro crates
run on the build host, not the embedded target). No cross-compilation is needed.
Toolchain
Stable Rust (pinned in rust-toolchain.toml at the repo root).
Changes MUST NOT break existing callers on the pinned stable channel.