macro_rules! embassy_method_accessor {
(
$struct_name: ident,
$(
(
$method_name: literal,
$function: path
)
)
, +
) => { ... };
}embassy-runtime only.Expand description
Helper macro for creating a PkMethodAccessor
backed by Embassy tasks.
§What This Macro Does
It provides a struct definition and implements the PkMethodAccessor
trait for it. The macro accepts several name-function pairs (5-character string literals paired
with async functions) and internally expands them into a simple mapping implementation based on
match statements. When invoked by PkCommand, it constructs an EmbassyPollable
struct and spawns the provided async task in the Embassy executor.
§Why a Macro is Needed
In embedded scenarios, memory and performance are typically constrained, making it wasteful to
maintain a HashMap resident in memory. Moreover, for async tasks
in the Embassy environment, the functions generated by #[task] return
a SpawnToken<S> (where S is an opaque type that differs for
each generated task — we only know it implements the Sized trait). If we followed a HashMap-like
approach and used wrappers like Box to force them into the same variable, it would introduce unnecessary
and relatively expensive runtime overhead and code complexity.
However, with a macro, we can simplify the complex hash matching logic into Rust’s built-in pattern
matching and hide the differences in SpawnToken<S> generic parameters through
different execution paths (from the compiler’s perspective). This not only significantly reduces runtime overhead
but also seamlessly integrates the Embassy environment into PK Command.
§How to Use This Macro
You can invoke this macro like:
embassy_method_accessor!(
MyMethodAccessor,
("TASK1", async_task_1),
("TASK2", async_task_2)
);The macro accepts parameters consisting of an identifier and several tuples (at least one). Specifically:
- Identifier: The name of the
PkMethodAccessorstruct to be generated. - Tuples:
- First element: A 5-character string literal indicating the method name.
- Second element: A function marked with the
#[embassy_executor::task]macro. This function must have the following form:
#[embassy_executor::task]
async fn async_task (param: Vec<u8>, callback: TaskCallback)See TaskCallback for more details.
The macro does not perform compile-time checks for this, but you should still note: method names must be 5-character strings containing only ASCII characters. If this constraint is not met, the code will still compile, but your registered methods may not be callable by PkCommand, which is a serious logical error.
§Complete Example
use embassy_executor::Spawner;
use embassy_time::Timer;
use pk_command::embassy_adapter::TaskCallback;
use pk_command::{EmbassyInstant, PkCommand, PkCommandConfig, PkHashmapVariable};
#[embassy_executor::task]
async fn async_task(param: Vec<u8>, callback: TaskCallback) {
// do_something_with(param);
callback(param);
}
// This is required for the macro to work.
extern crate alloc;
pk_command::embassy_method_accessor!(MyMethodAccessor, ("TASK1", async_task));
#[embassy_executor::task]
async fn pk_command(pk: PkCommand<PkHashmapVariable, MyMethodAccessor, EmbassyInstant>) {
loop {
let cmd = pk.poll();
if let Some(cmd) = cmd {
// send to the transport layer..
}
Timer::after_millis(10).await;
}
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
let send_spawner = spawner.make_send();
let ma = MyMethodAccessor::new(send_spawner);
// Here you can use `ma` as the MethodAccessor of PkCommand
let pk = PkCommand::<_, _, EmbassyInstant>::new(
PkCommandConfig::default(64),
PkHashmapVariable::new(vec![]),
ma,
);
spawner.spawn(pk_command(pk)).unwrap();
}