use crate::error::Error;
use crate::helpers::max_usize;
use core::num::NonZeroU16;
use minicbor::{Decode, Encode};
use oc_wasm_futures::invoke::{component_method, Buffer};
use oc_wasm_helpers::Lockable;
use oc_wasm_safe::{
component::{Invoker, MethodCallError},
extref, Address,
};
pub const TYPE: &str = "modem";
#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)]
pub enum PacketPart<'a> {
Null,
Bool(bool),
I32(i32),
F64(f64),
Str(&'a str),
Bytes(&'a [u8]),
}
impl From<()> for PacketPart<'_> {
fn from(_: ()) -> Self {
Self::Null
}
}
impl From<bool> for PacketPart<'_> {
fn from(value: bool) -> Self {
Self::Bool(value)
}
}
impl From<u8> for PacketPart<'_> {
fn from(value: u8) -> Self {
Self::I32(value.into())
}
}
impl From<u16> for PacketPart<'_> {
fn from(value: u16) -> Self {
Self::I32(value.into())
}
}
impl From<u32> for PacketPart<'_> {
fn from(value: u32) -> Self {
match TryInto::<i32>::try_into(value) {
Ok(n) => Self::I32(n),
Err(_) => Self::F64(value.into()),
}
}
}
impl From<i8> for PacketPart<'_> {
fn from(value: i8) -> Self {
Self::I32(value.into())
}
}
impl From<i16> for PacketPart<'_> {
fn from(value: i16) -> Self {
Self::I32(value.into())
}
}
impl From<i32> for PacketPart<'_> {
fn from(value: i32) -> Self {
Self::I32(value)
}
}
impl From<f32> for PacketPart<'_> {
fn from(value: f32) -> Self {
Self::F64(value.into())
}
}
impl From<f64> for PacketPart<'_> {
fn from(value: f64) -> Self {
Self::F64(value)
}
}
impl<'a> From<&'a str> for PacketPart<'a> {
fn from(value: &'a str) -> Self {
Self::Str(value)
}
}
impl<'a> From<&'a [u8]> for PacketPart<'a> {
fn from(value: &'a [u8]) -> Self {
Self::Bytes(value)
}
}
impl<'a, const N: usize> From<&'a [u8; N]> for PacketPart<'a> {
fn from(value: &'a [u8; N]) -> Self {
Self::Bytes(value)
}
}
impl<'a, const N: usize> From<&'a minicbor::bytes::ByteArray<N>> for PacketPart<'a> {
fn from(value: &'a minicbor::bytes::ByteArray<N>) -> Self {
Self::Bytes(&**value)
}
}
impl<'a> From<&'a minicbor::bytes::ByteSlice> for PacketPart<'a> {
fn from(value: &'a minicbor::bytes::ByteSlice) -> Self {
Self::Bytes(value)
}
}
impl<'a> From<&'a minicbor::bytes::ByteVec> for PacketPart<'a> {
fn from(value: &'a minicbor::bytes::ByteVec) -> Self {
Self::Bytes(value)
}
}
impl<'buffer, Context> Decode<'buffer, Context> for PacketPart<'buffer> {
fn decode(
d: &mut minicbor::decode::Decoder<'buffer>,
_: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
use minicbor::data::Type;
match d.datatype()? {
Type::Bool => Ok(Self::Bool(d.bool()?)),
Type::Null => {
d.null()?;
Ok(Self::Null)
}
Type::Undefined => {
d.undefined()?;
Ok(Self::Null)
}
Type::F16 | Type::F32 | Type::F64 => Ok(Self::F64(d.f64()?)),
Type::Bytes => Ok(Self::Bytes(d.bytes()?)),
Type::String => Ok(Self::Str(d.str()?)),
t => Err(minicbor::decode::Error::type_mismatch(t)),
}
}
fn nil() -> Option<Self> {
Some(Self::Null)
}
}
impl<Context> Encode<Context> for PacketPart<'_> {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
c: &mut Context,
) -> Result<(), minicbor::encode::Error<W::Error>> {
match *self {
Self::Null => e.null(),
Self::Bool(v) => e.bool(v),
Self::I32(v) => e.i32(v),
Self::F64(v) => e.f64(v),
Self::Str(v) => e.encode_with(unsafe { extref::String::new(v) }, c),
Self::Bytes(v) => e.encode_with(unsafe { extref::Bytes::new(v) }, c),
}?;
Ok(())
}
fn is_nil(&self) -> bool {
*self == Self::Null
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum WakeMessage<'a> {
Disabled,
Exact(&'a str),
Fuzzy(&'a str),
}
impl<'buffer, Context> Decode<'buffer, Context> for WakeMessage<'buffer> {
fn decode(
d: &mut minicbor::Decoder<'buffer>,
context: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
let inner = <(Option<&'buffer str>, bool)>::decode(d, context)?;
match inner {
(None, _) => Ok(Self::Disabled),
(Some(s), false) => Ok(Self::Exact(s)),
(Some(s), true) => Ok(Self::Fuzzy(s)),
}
}
}
impl<Context> Encode<Context> for WakeMessage<'_> {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
context: &mut Context,
) -> Result<(), minicbor::encode::Error<W::Error>> {
let inner: (Option<extref::String<'_>>, bool) = match self {
Self::Disabled => (None, false),
Self::Exact(s) => (Some(unsafe { extref::String::new(s) }), false),
Self::Fuzzy(s) => (Some(unsafe { extref::String::new(s) }), true),
};
inner.encode(e, context)
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Modem(Address);
impl Modem {
#[must_use = "This function is only useful for its return value"]
pub fn new(address: Address) -> Self {
Self(address)
}
#[must_use = "This function is only useful for its return value"]
pub fn address(&self) -> &Address {
&self.0
}
}
impl<'invoker, 'buffer, B: 'buffer + Buffer> Lockable<'invoker, 'buffer, B> for Modem {
type Locked = Locked<'invoker, 'buffer, B>;
fn lock(&self, invoker: &'invoker mut Invoker, buffer: &'buffer mut B) -> Self::Locked {
Locked {
address: self.0,
invoker,
buffer,
}
}
}
pub struct Locked<'invoker, 'buffer, B> {
address: Address,
invoker: &'invoker mut Invoker,
buffer: &'buffer mut B,
}
impl<'invoker, 'buffer, B: Buffer> Locked<'invoker, 'buffer, B> {
pub async fn is_wireless(&mut self) -> Result<bool, Error> {
let ret: (bool,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"isWireless",
None,
)
.await?;
Ok(ret.0)
}
pub async fn is_open(&mut self, port: NonZeroU16) -> Result<bool, Error> {
let ret: (bool,) = component_method(
self.invoker,
self.buffer,
&self.address,
"isOpen",
Some(&(port,)),
)
.await?;
Ok(ret.0)
}
pub async fn open(&mut self, port: NonZeroU16) -> Result<bool, Error> {
let ret: Result<(bool,), MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.address,
"open",
Some(&(port,)),
)
.await;
match ret {
Ok((ret,)) => Ok(ret),
Err(MethodCallError::Other(exception)) => {
if exception.is_type("java.io.IOException") {
const TOO_MANY_OPEN_PORTS: &str = "too many open ports";
let mut message_buffer = [0_u8; TOO_MANY_OPEN_PORTS.len()];
match exception.message(&mut message_buffer) {
Ok(TOO_MANY_OPEN_PORTS) => Err(Error::TooManyOpenPorts),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
} else {
Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown))
}
}
Err(e) => Err(e.into()),
}
}
pub async fn close(&mut self, port: NonZeroU16) -> Result<bool, Error> {
let ret: (bool,) = component_method(
self.invoker,
self.buffer,
&self.address,
"close",
Some(&(port,)),
)
.await?;
Ok(ret.0)
}
pub async fn close_all(&mut self) -> Result<bool, Error> {
let ret: (bool,) =
component_method::<(), _, _>(self.invoker, self.buffer, &self.address, "close", None)
.await?;
Ok(ret.0)
}
pub async fn send(
&mut self,
destination: Address,
port: NonZeroU16,
parts: &[PacketPart<'_>],
) -> Result<(), Error> {
struct Params<'parts> {
destination: Address,
port: NonZeroU16,
parts: &'parts [PacketPart<'parts>],
}
impl<Context> Encode<Context> for Params<'_> {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
context: &mut Context,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(2 + self.parts.len() as u64)?;
self.destination.encode(e, context)?;
self.port.encode(e, context)?;
for part in self.parts {
part.encode(e, context)?;
}
Ok(())
}
}
self.do_send(
"send",
&Params {
destination,
port,
parts,
},
)
.await
}
pub async fn broadcast(
&mut self,
port: NonZeroU16,
parts: &[PacketPart<'_>],
) -> Result<(), Error> {
struct Params<'parts> {
port: NonZeroU16,
parts: &'parts [PacketPart<'parts>],
}
impl<Context> Encode<Context> for Params<'_> {
fn encode<W: minicbor::encode::Write>(
&self,
e: &mut minicbor::Encoder<W>,
context: &mut Context,
) -> Result<(), minicbor::encode::Error<W::Error>> {
e.array(1 + self.parts.len() as u64)?;
self.port.encode(e, context)?;
for part in self.parts {
part.encode(e, context)?;
}
Ok(())
}
}
self.do_send("broadcast", &Params { port, parts }).await
}
async fn do_send<P: Encode<()>>(&mut self, method: &str, params: &P) -> Result<(), Error> {
let ret: Result<(bool,), MethodCallError<'_>> = component_method(
self.invoker,
self.buffer,
&self.address,
method,
Some(params),
)
.await;
match ret {
Ok(_) => Ok(()),
Err(MethodCallError::BadParameters(exception)) => {
const PACKET_HAS_TOO_MANY_PARTS: &str = "packet has too many parts";
const PACKET_TOO_BIG_PREFIX: &str = "packet too big";
const MESSAGE_BUFFER_SIZE: usize = max_usize(
PACKET_HAS_TOO_MANY_PARTS.len(),
PACKET_TOO_BIG_PREFIX.len() + 20,
);
let mut message_buffer = [0_u8; MESSAGE_BUFFER_SIZE];
match exception.message(&mut message_buffer) {
Ok(PACKET_HAS_TOO_MANY_PARTS) => Err(Error::TooManyParts),
Ok(m) if m.starts_with(PACKET_TOO_BIG_PREFIX) => Err(Error::DataTooLarge),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
}
Err(e @ MethodCallError::Other(exception)) => {
if exception.is_type("java.io.IOException") {
const NOT_ENOUGH_ENERGY: &str = "not enough energy";
let mut message_buffer = [0_u8; NOT_ENOUGH_ENERGY.len()];
match exception.message(&mut message_buffer) {
Ok(NOT_ENOUGH_ENERGY) => Err(Error::NotEnoughEnergy),
_ => Err(Error::BadComponent(oc_wasm_safe::error::Error::Unknown)),
}
} else {
Err(e.into())
}
}
Err(e) => Err(e.into()),
}
}
pub async fn get_strength(&mut self) -> Result<f64, Error> {
let ret: (f64,) = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getStrength",
None,
)
.await?;
Ok(ret.0)
}
pub async fn set_strength(&mut self, strength: f64) -> Result<f64, Error> {
let ret: (f64,) = component_method(
self.invoker,
self.buffer,
&self.address,
"setStrength",
Some(&(strength,)),
)
.await?;
Ok(ret.0)
}
pub async fn get_wake_message(self) -> Result<WakeMessage<'buffer>, Error> {
let ret = component_method::<(), _, _>(
self.invoker,
self.buffer,
&self.address,
"getWakeMessage",
None,
)
.await?;
Ok(ret)
}
pub async fn set_wake_message(
self,
config: WakeMessage<'_>,
) -> Result<WakeMessage<'buffer>, Error> {
let ret = component_method(
self.invoker,
self.buffer,
&self.address,
"setWakeMessage",
Some(&config),
)
.await?;
Ok(ret)
}
}
pub const MESSAGE_NAME: &str = "modem_message";
#[derive(Clone, Debug, PartialEq, PartialOrd)]
pub struct Message<'buffer, const N: usize> {
pub receiver: Address,
pub sender: Address,
pub port: u16,
pub distance: f64,
pub parts: [PacketPart<'buffer>; N],
}
impl<'buffer, Context, const N: usize> Decode<'buffer, Context> for Message<'buffer, N> {
fn decode(
d: &mut minicbor::Decoder<'buffer>,
context: &mut Context,
) -> Result<Self, minicbor::decode::Error> {
#[allow(clippy::cast_possible_truncation)]
let len = d.array()?.unwrap() as usize;
if len >= 4 {
let parts_count = len - 4;
if parts_count <= N {
let receiver = Address::decode(d, context)?;
let sender = Address::decode(d, context)?;
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
let port = d.f64()? as u16;
let distance = d.f64()?;
let mut parts = [PacketPart::Null; N];
for i in parts[..parts_count].iter_mut() {
*i = PacketPart::decode(d, context)?;
}
Ok(Self {
receiver,
sender,
port,
distance,
parts,
})
} else {
Err(minicbor::decode::Error::message("too many parts"))
}
} else {
Err(minicbor::decode::Error::message("array too short"))
}
}
}