use serde::Serialize;
use serde::de::DeserializeOwned;
use crate::{Bytes, Result};
use alloc::string::String;
use myrmic_common::cells::Sri;
pub trait Decoder: Sized {
fn from_args(length: usize) -> Result<Self> {
let mut bytes = alloc::vec![0u8; length];
let n = crate::get_arguments(&mut bytes).map_err(|_| "failed to read arguments")?;
bytes.truncate(n);
let absent = bytes.is_empty();
Self::from_bytes(bytes).map_err(|err| {
if absent {
"no payload was sent; declare the handler's payload as `Option<_>` \
to accept an invocation sent without one"
} else {
err
}
})
}
fn from_bytes(bytes: Bytes) -> Result<Self>;
}
pub trait Encoder {
fn to_bytes(&self) -> Result<Bytes>;
}
pub trait Codec {
fn encode<T: Serialize + ?Sized>(value: &T) -> Result<Bytes>;
fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Result<T>;
}
pub struct Json;
impl Codec for Json {
fn encode<T: Serialize + ?Sized>(value: &T) -> Result<Bytes> {
serde_json::to_vec(value).map_err(|_| "failed to serialize json")
}
fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
serde_json::from_slice(bytes).map_err(|_| "failed to deserialize json")
}
}
pub struct Postcard;
impl Codec for Postcard {
fn encode<T: Serialize + ?Sized>(value: &T) -> Result<Bytes> {
postcard::to_allocvec(value).map_err(|_| "failed to serialize postcard")
}
fn decode<T: DeserializeOwned>(bytes: &[u8]) -> Result<T> {
postcard::from_bytes(bytes).map_err(|_| "failed to deserialize postcard")
}
}
impl Decoder for Bytes {
fn from_bytes(bytes: Bytes) -> Result<Self> {
Ok(bytes)
}
}
impl Encoder for Bytes {
fn to_bytes(&self) -> Result<Bytes> {
Ok(self.clone())
}
}
impl Decoder for Sri {
fn from_bytes(bytes: Bytes) -> Result<Self> {
let (hi, lo): (i64, i64) = <Postcard as Codec>::decode(&bytes)?;
Ok(Sri::from_parts(hi, lo))
}
}
impl Encoder for Sri {
fn to_bytes(&self) -> Result<Bytes> {
let parts = self.to_parts();
<Postcard as Codec>::encode(&parts)
}
}
pub struct Void;
impl Decoder for Void {
fn from_args(length: usize) -> Result<Self> {
if length == 0 {
Ok(Void)
} else {
Err("this handler does not accept a payload")
}
}
fn from_bytes(bytes: Bytes) -> Result<Self> {
if bytes.is_empty() {
Ok(Void)
} else {
Err("this handler does not accept a payload")
}
}
}
impl Encoder for Void {
fn to_bytes(&self) -> Result<Bytes> {
Ok(Bytes::new())
}
}
impl<T: Decoder> Decoder for Option<T> {
fn from_args(length: usize) -> Result<Self> {
if length == 0 {
Ok(None)
} else {
T::from_args(length).map(Some)
}
}
fn from_bytes(bytes: Bytes) -> Result<Self> {
if bytes.is_empty() {
Ok(None)
} else {
T::from_bytes(bytes).map(Some)
}
}
}
impl<T: Encoder> Encoder for Option<T> {
fn to_bytes(&self) -> Result<Bytes> {
match self {
Some(value) => T::to_bytes(value),
None => Ok(Bytes::new()),
}
}
}
macro_rules! json_scalar {
($($ty:ty),* $(,)?) => {$(
impl Decoder for $ty {
fn from_bytes(bytes: Bytes) -> Result<Self> {
<Json as Codec>::decode(&bytes)
}
}
impl Encoder for $ty {
fn to_bytes(&self) -> Result<Bytes> {
<Json as Codec>::encode(self)
}
}
)*};
}
json_scalar!(String, bool, char, f32, f64);
macro_rules! json_int {
($($ty:ty),* $(,)?) => {$(
impl Decoder for $ty {
fn from_bytes(bytes: Bytes) -> Result<Self> {
if let Ok(value) = <Json as Codec>::decode::<$ty>(&bytes) {
return Ok(value);
}
let number: serde_json::Number = <Json as Codec>::decode(&bytes)?;
let f = number.as_f64().ok_or("expected a number")?;
if f as i128 as f64 == f && f >= <$ty>::MIN as f64 && f <= <$ty>::MAX as f64 {
Ok(f as $ty)
} else {
Err("number is not a whole value in range for the target type")
}
}
}
impl Encoder for $ty {
fn to_bytes(&self) -> Result<Bytes> {
<Json as Codec>::encode(self)
}
}
)*};
}
json_int!(u8, u16, u32, u64, u128, i8, i16, i32, i64, i128,);
#[cfg(test)]
mod tests {
use core::ffi::c_int;
use spin::Mutex;
use super::{Decoder, Encoder};
use crate::{Bytes, Callback, JsonValue, Result};
use alloc::string::String;
use alloc::vec::Vec;
use myrmic_common::cells::Command;
fn dec<T: Decoder>(bytes: &[u8]) -> Result<T> {
T::from_bytes(Bytes::from(bytes))
}
fn enc<T: Encoder>(value: &T) -> Vec<u8> {
value.to_bytes().unwrap()
}
fn enc_str<T: Encoder>(value: &T) -> String {
String::from_utf8(enc(value)).unwrap()
}
#[test]
fn integer_encodes_as_json_number() {
assert_eq!(enc_str(&42u32), "42");
}
#[test]
fn json_number_decodes_into_integer() {
assert_eq!(dec::<u32>(b"42").unwrap(), 42);
}
#[test]
fn integral_float_decodes_into_integer() {
assert_eq!(dec::<u32>(b"42.0").unwrap(), 42);
}
#[test]
fn fractional_number_rejected_for_integer() {
assert!(dec::<u32>(b"42.5").is_err());
}
#[test]
fn out_of_range_number_rejected_for_integer() {
assert!(dec::<u8>(b"300").is_err());
}
#[test]
fn signed_integer_round_trips() {
assert_eq!(dec::<i64>(&enc(&-5i64)).unwrap(), -5);
}
#[test]
fn max_u128_round_trips_exactly() {
let v = u128::MAX;
assert_eq!(dec::<u128>(&enc(&v)).unwrap(), v);
}
#[test]
fn float_decodes_from_any_json_number() {
assert_eq!(dec::<f64>(b"42").unwrap(), 42.0);
assert_eq!(dec::<f32>(b"1.5").unwrap(), 1.5);
}
#[test]
fn float_encodes_as_json_number() {
assert_eq!(enc_str(&1.5f64), "1.5");
}
#[test]
fn bool_round_trips_as_json() {
assert_eq!(enc_str(&true), "true");
assert!(dec::<bool>(b"true").unwrap());
}
#[test]
fn char_round_trips_as_json_string() {
assert_eq!(enc_str(&'a'), "\"a\"");
assert_eq!(dec::<char>(b"\"a\"").unwrap(), 'a');
}
#[test]
fn string_round_trips_as_json_string() {
assert_eq!(enc_str(&String::from("jsontest")), "\"jsontest\"");
assert_eq!(dec::<String>(b"\"jsontest\"").unwrap(), "jsontest");
}
#[test]
fn optional_callback_absorbs_only_the_empty_buffer() {
assert!(dec::<Option<Callback<JsonValue>>>(b"").unwrap().is_none());
let decoded = dec::<Option<Callback<JsonValue>>>(b"on_reply").unwrap();
assert_eq!(Command::from(decoded.unwrap()).as_ref(), "on_reply");
assert!(dec::<Option<Callback<JsonValue>>>(b"\"on_reply\"").is_err());
assert!(dec::<Callback<JsonValue>>(b"").is_err());
}
#[test]
fn zero_length_from_args_never_touches_the_inner_decoder() {
assert!(<Option<Probe>>::from_args(0).unwrap().is_none());
assert!(<Option<Probe>>::from_bytes(Bytes::new()).unwrap().is_none());
}
#[test]
fn an_absent_payload_is_reported_with_advice() {
assert_eq!(
from_args_with::<u32>(b"").unwrap_err(),
"no payload was sent; declare the handler's payload as `Option<_>` \
to accept an invocation sent without one"
);
assert_eq!(
from_args_with::<Callback<JsonValue>>(b"").unwrap_err(),
"no payload was sent; declare the handler's payload as `Option<_>` \
to accept an invocation sent without one"
);
assert!(from_args_with::<Option<u32>>(b"").unwrap().is_none());
assert_eq!(from_args_with::<Option<u32>>(b"42").unwrap(), Some(42));
assert_eq!(
from_args_with::<u32>(b"42.5").unwrap_err(),
"number is not a whole value in range for the target type"
);
assert_eq!(
from_args_with::<Callback<JsonValue>>(b"\"on_reply\"").unwrap_err(),
"name can only contain ASCII alphanumeric characters and underscores"
);
}
struct Probe;
impl Decoder for Probe {
fn from_args(_length: usize) -> Result<Self> {
panic!("a zero-length argument buffer was delegated to the inner decoder");
}
fn from_bytes(_bytes: Bytes) -> Result<Self> {
panic!("an empty byte buffer was delegated to the inner decoder");
}
}
fn from_args_with<T: Decoder>(payload: &[u8]) -> Result<T> {
*ARGUMENTS.lock() = payload.to_vec();
T::from_args(payload.len())
}
static ARGUMENTS: Mutex<Vec<u8>> = Mutex::new(Vec::new());
#[unsafe(no_mangle)]
extern "C" fn get_arguments(buffer: *mut u8, length: c_int) -> c_int {
let payload = ARGUMENTS.lock();
let n = payload.len().min(length as usize);
unsafe { core::ptr::copy_nonoverlapping(payload.as_ptr(), buffer, n) };
n as c_int
}
}