use rmk::event::{
ConnectionStatusChangeEvent, LedIndicatorEvent, SleepStateEvent, WpmUpdateEvent, publish_event_async,
};
use rmk::k;
use rmk::test_support::test_block_on;
use rmk::types::keycode::HidKeyCode;
use rmk_types::connection::ConnectionStatus;
use rmk_types::led_indicator::LedIndicator;
use rmk_types::protocol::rynk::command::Endpoint;
use rmk_types::protocol::rynk::{Cmd, RynkError, RynkHeader, command, encode_frame};
use crate::simulator::SimKeyboard;
pub(crate) enum RynkReply<'a> {
Ok(&'a str),
Err(RynkError),
}
impl SimKeyboard {
pub(crate) fn rynk<E: Endpoint>(&mut self, request: &str, reply: RynkReply<'_>) -> &mut Self {
let request = frame(E::CMD, 1, &payload::<E::Request>(request, "request"));
match reply {
RynkReply::Ok(response) => {
let response = payload::<E::Response>(response, "response");
self.host_exchange(request, frame(E::CMD, 1, &Ok::<_, RynkError>(response)))
}
RynkReply::Err(error) => self.host_exchange(request, frame(E::CMD, 1, &Err::<E::Response, _>(error))),
}
}
pub(crate) fn rynk_topic(&mut self, cmd: Cmd, json: &str) -> &mut Self {
let expected = match cmd {
Cmd::LayerChange => frame(cmd, 0, &payload::<u8>(json, "topic")),
Cmd::WpmUpdate => frame(cmd, 0, &payload::<u16>(json, "topic")),
Cmd::ConnectionChange => frame(cmd, 0, &payload::<ConnectionStatus>(json, "topic")),
Cmd::SleepState => frame(cmd, 0, &payload::<bool>(json, "topic")),
Cmd::LedIndicatorChange => frame(cmd, 0, &payload::<LedIndicator>(json, "topic")),
#[cfg(feature = "_ble")]
Cmd::BatteryStatusChange => frame(cmd, 0, &payload::<rmk_types::battery::BatteryStatus>(json, "topic")),
cmd => panic!("{cmd:?} is not a topic"),
};
self.expect_host_frame(expected)
}
pub(crate) fn rynk_publish(&mut self, cmd: Cmd, json: &str) -> &mut Self {
match cmd {
Cmd::WpmUpdate => self.publish(publish_event_async(WpmUpdateEvent(payload(json, "topic")))),
Cmd::SleepState => self.publish(publish_event_async(SleepStateEvent(payload(json, "topic")))),
Cmd::LedIndicatorChange => self.publish(publish_event_async(LedIndicatorEvent(payload(json, "topic")))),
Cmd::ConnectionChange => {
self.publish(publish_event_async(ConnectionStatusChangeEvent(payload(json, "topic"))))
}
#[cfg(feature = "_ble")]
Cmd::BatteryStatusChange => self.publish(publish_event_async(rmk::event::BatteryStatusEvent(payload(
json, "topic",
)))),
cmd => panic!("no internal event feeds topic {cmd:?}"),
}
}
}
fn frame<T: serde::Serialize>(cmd: Cmd, seq: u8, payload: &T) -> Vec<u8> {
let mut buf = [0; rmk_types::constants::RYNK_BUFFER_SIZE];
let n = encode_frame(&mut buf, RynkHeader { cmd, seq }, payload).unwrap();
buf[..n].to_vec()
}
fn payload<T: serde::de::DeserializeOwned>(json: &str, what: &str) -> T {
serde_json::from_str(json).unwrap_or_else(|e| panic!("rynk {what} payload `{json}`: {e}"))
}
#[cfg(feature = "storage")]
#[test]
fn keymap_write_survives_restart() {
const SET_KEY_B: &str = r#"{"position":{"layer":0,"row":0,"col":0},"action":{"Single":{"Key":{"Hid":"B"}}}}"#;
test_block_on(async {
let flash = crate::simulator::flash::InMemoryFlash::new();
{
let mut keyboard = SimKeyboard::builder([[[k!(A)]]]).build_with_flash(flash.clone()).await;
keyboard
.rynk::<command::SetKeyAction>(SET_KEY_B, RynkReply::Ok("null"))
.wait_storage()
.run()
.await;
}
let mut keyboard = SimKeyboard::builder([[[k!(A)]]]).build_with_flash(flash).await;
keyboard
.tap(0, 0, 10)
.expect_keys([HidKeyCode::B])
.expect_keys([])
.run()
.await;
});
}