#[cfg(feature = "std")]
use std::sync::{Arc, RwLock};
#[cfg(feature = "std")]
use std::{cell::RefCell, pin::Pin};
pub mod msg_id {
#[cfg(not(feature = "std"))]
use alloc::{format, string::String};
const BASE: u16 = 94;
const OFFSET: u8 = b'!';
const MAX_ID: u16 = BASE * BASE - 1;
pub fn to_u16(id_str: &str) -> Result<u16, &'static str> {
if id_str.len() != 2 {
return Err("Input string must be exactly 2 characters long.");
}
let bytes = id_str.as_bytes();
let c1 = bytes[0];
let c2 = bytes[1];
if !((b'!'..=b'~').contains(&c1) && (b'!'..=b'~').contains(&c2)) {
return Err("Input string contains invalid characters.");
}
let val1 = (c1 - OFFSET) as u16;
let val2 = (c2 - OFFSET) as u16;
Ok(val1 * BASE + val2)
}
pub fn from_u16(id: u16) -> Result<String, &'static str> {
if id > MAX_ID {
return Err("Input number is out of the valid range (0-8835).");
}
let val1 = id / BASE;
let val2 = id % BASE;
let c1 = (val1 as u8 + OFFSET) as char;
let c2 = (val2 as u8 + OFFSET) as char;
Ok(format!("{}{}", c1, c2))
}
pub fn increment(id: u16) -> u16 {
(id + 1) % (MAX_ID + 1)
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(not(feature = "std"))]
use alloc::string::ToString;
#[test]
fn test_msg_id_to_u16_valid() {
assert_eq!(to_u16("!!"), Ok(0));
assert_eq!(to_u16("!\""), Ok(1));
assert_eq!(to_u16("\"!"), Ok(BASE));
assert_eq!(to_u16("~~"), Ok(MAX_ID));
}
#[test]
fn test_msg_id_to_u16_invalid_length() {
assert!(to_u16("!").is_err());
assert!(to_u16("!!!").is_err());
}
#[test]
fn test_msg_id_to_u16_invalid_chars() {
assert!(to_u16(" !").is_err()); }
#[test]
fn test_msg_id_from_u16_valid() {
assert_eq!(from_u16(0), Ok("!!".to_string()));
assert_eq!(from_u16(1), Ok("!\"".to_string()));
assert_eq!(from_u16(BASE), Ok("\"!".to_string()));
assert_eq!(from_u16(MAX_ID), Ok("~~".to_string()));
}
#[test]
fn test_msg_id_from_u16_out_of_range() {
assert!(from_u16(MAX_ID + 1).is_err());
}
#[test]
fn test_msg_id_increment() {
assert_eq!(increment(0), 1);
assert_eq!(increment(MAX_ID), 0); assert_eq!(increment(100), 101);
}
}
}
pub mod async_adapters {
#[cfg(all(feature = "std", feature = "tokio-runtime"))]
pub mod tokio {
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
#[allow(clippy::type_complexity)]
pub struct TokioFuturePollable {
state: Arc<RwLock<Option<Result<Option<Vec<u8>>, String>>>>,
}
impl TokioFuturePollable {
pub fn from_future<F>(fut: F) -> Pin<Box<dyn crate::Pollable>>
where
F: Future<Output = Result<Option<Vec<u8>>, String>> + Send + 'static,
{
let state = Arc::new(RwLock::new(None));
let state_cloned = state.clone();
tokio::spawn(async move {
let res = fut.await;
*state_cloned.write().unwrap() = Some(res);
});
Box::pin(TokioFuturePollable { state })
}
}
#[cfg(all(feature = "std", feature = "tokio-runtime"))] impl crate::Pollable for TokioFuturePollable {
fn poll(&self) -> std::task::Poll<Result<Option<Vec<u8>>, String>> {
match self.state.read().unwrap().as_ref() {
Some(r) => std::task::Poll::Ready(r.clone()),
None => std::task::Poll::Pending,
}
}
}
}
#[cfg(all(feature = "std", feature = "smol-runtime"))]
pub mod smol {
use std::future::Future;
use std::pin::Pin;
use std::sync::{Arc, RwLock};
#[allow(clippy::type_complexity)]
pub struct SmolFuturePollable {
state: Arc<RwLock<Option<Result<Option<Vec<u8>>, String>>>>,
}
impl SmolFuturePollable {
pub fn from_future<F>(fut: F) -> Pin<Box<dyn crate::Pollable>>
where
F: Future<Output = Result<Option<Vec<u8>>, String>> + Send + 'static,
{
let state = Arc::new(RwLock::new(None));
let state_cloned = state.clone();
smol::spawn(async move {
let res = fut.await;
*state_cloned.write().unwrap() = Some(res);
})
.detach();
Box::pin(SmolFuturePollable { state })
}
}
#[cfg(all(feature = "std", feature = "smol-runtime"))]
impl crate::Pollable for SmolFuturePollable {
fn poll(&self) -> std::task::Poll<Result<Option<Vec<u8>>, String>> {
match self.state.read().unwrap().as_ref() {
Some(r) => std::task::Poll::Ready(r.clone()),
None => std::task::Poll::Pending,
}
}
}
}
#[cfg(feature = "embassy-runtime")]
pub mod embassy {
extern crate alloc;
use alloc::sync::Arc;
use alloc::vec::Vec;
use embassy_sync::once_lock::OnceLock;
pub struct EmbassyPollable(pub Arc<OnceLock<Vec<u8>>>);
impl crate::Pollable for EmbassyPollable {
fn poll(&self) -> core::task::Poll<Result<Option<Vec<u8>>, String>> {
match self.0.try_get() {
Some(data) => core::task::Poll::Ready(Ok(Some(data.clone()))),
None => core::task::Poll::Pending,
}
}
}
pub type TaskCallback = Box<dyn Fn(Vec<u8>) + Send>;
#[cfg_attr(docsrs, doc(cfg(feature = "embassy-runtime")))]
#[macro_export]
macro_rules! embassy_method_accessor {
(
$struct_name: ident,
$(
(
$method_name: literal,
$function: path
)
)
, +
) => {
#[derive(Clone, Copy)]
struct $struct_name
{
spawner: ::embassy_executor::SendSpawner,
}
impl ::pk_command::PkMethodAccessor for $struct_name
{
fn call(
&self,
key: ::alloc::string::String,
param: ::alloc::vec::Vec<u8>
) -> ::core::result::Result<::core::pin::Pin<::alloc::boxed::Box<dyn ::pk_command::Pollable>>, ::alloc::string::String>
{
let lock=::alloc::sync::Arc::new(::embassy_sync::once_lock::OnceLock::new());
let lock_clone=lock.clone();
let pollable=::alloc::boxed::Box::pin(::pk_command::embassy_adapter::EmbassyPollable(lock_clone));
let lock_clone_clone=lock.clone();
let callback = ::alloc::boxed::Box::new(move |data: Vec<u8>| {
lock_clone_clone.init(data).unwrap();
});
match key.as_str() {
$(
$method_name => {
let token=$function(param, callback);
self.spawner.spawn(token)
.map_err(|x| x.to_string())?;
Ok(pollable)
},
)*
_ => {
let mut err_msg = ::alloc::string::String::from("No method named ");
err_msg.push_str(&key);
err_msg.push_str(" found");
Err(err_msg)
}
}
}
}
impl $struct_name
{
fn new(spawner: ::embassy_executor::SendSpawner) -> Self
{
Self {spawner}
}
}
};
}
}
}
#[cfg(feature = "std")]
pub type VariableChangeListener = Box<dyn Fn(Vec<u8>)>;
#[cfg(feature = "std")]
pub struct PkHashmapVariable {
hashmap: std::collections::HashMap<String, (RefCell<Vec<u8>>, VariableChangeListener)>,
}
#[cfg(feature = "std")]
impl crate::PkVariableAccessor for PkHashmapVariable {
fn get(&self, key: String) -> Option<Vec<u8>> {
self.hashmap.get(&key).map(|v| v.0.borrow().clone())
}
fn set(&self, key: String, value: Vec<u8>) -> Result<(), String> {
if self.hashmap.contains_key(&key) {
let v = self.hashmap.get(&key).unwrap();
v.0.replace(value.clone());
v.1(value);
Ok(())
} else {
Err(String::from("Key not found"))
}
}
}
#[cfg(feature = "std")]
impl PkHashmapVariable {
pub fn new(init_vec: Vec<(String, Option<Vec<u8>>, VariableChangeListener)>) -> Self {
let mut hashmap = std::collections::HashMap::new();
for i in init_vec.into_iter() {
let (key, value, listener) = i;
hashmap.insert(key, (RefCell::new(value.unwrap_or_default()), listener));
}
PkHashmapVariable { hashmap }
}
}
#[cfg(feature = "std")]
pub type MethodImplementation = Box<dyn Fn(Option<Vec<u8>>) -> Pin<Box<dyn crate::Pollable>>>;
#[cfg(feature = "std")]
pub struct PkHashmapMethod {
hashmap: std::collections::HashMap<String, MethodImplementation>,
}
#[cfg(feature = "std")]
impl crate::PkMethodAccessor for PkHashmapMethod {
fn call(&self, key: String, param: Vec<u8>) -> Result<Pin<Box<dyn crate::Pollable>>, String> {
if self.hashmap.contains_key(&key) {
let f = self.hashmap.get(&key).unwrap();
Ok(f(Some(param)))
} else {
Err(String::from("Method not found"))
}
}
}
#[cfg(feature = "std")]
impl PkHashmapMethod {
pub fn new(init_vec: Vec<(String, MethodImplementation)>) -> Self {
let mut hashmap = std::collections::HashMap::new();
for i in init_vec.into_iter() {
let (key, method) = i;
hashmap.insert(key, method);
}
PkHashmapMethod { hashmap }
}
}
#[derive(Clone)]
#[cfg(feature = "std")]
pub struct PkPromise {
return_value: Arc<RwLock<Option<Vec<u8>>>>,
}
#[cfg(feature = "std")]
impl PkPromise {
#[cfg(feature = "std")]
pub fn execute<T>(function: T) -> Pin<Box<Self>>
where
T: FnOnce(Box<dyn FnOnce(Vec<u8>) + Send + 'static>) + Send + 'static,
{
let return_value_arc = Arc::new(RwLock::new(None));
let return_value_clone = return_value_arc.clone();
std::thread::spawn(move || {
let resolve: Box<dyn FnOnce(Vec<u8>) + Send + 'static> =
Box::new(move |ret: Vec<u8>| {
*return_value_clone.write().unwrap() = Some(ret);
});
function(Box::new(resolve));
});
Box::pin(PkPromise {
return_value: return_value_arc,
})
}
}
#[cfg(feature = "std")]
impl crate::Pollable for PkPromise {
fn poll(&self) -> std::task::Poll<Result<Option<Vec<u8>>, String>> {
let read_guard = self.return_value.read().unwrap();
match read_guard.as_ref() {
Some(data) => std::task::Poll::Ready(Ok(Some(data.clone()))),
None => std::task::Poll::Pending,
}
}
}