use core::cell::Cell;
use core::fmt;
use crate::im::encoding::{CmdPath, CmdStatus, IMStatusCode};
use super::{Access, ClusterId, CmdId, EndptId};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Command {
pub id: CmdId,
pub resp_id: Option<CmdId>,
pub access: Access,
}
impl Command {
pub const fn new(id: CmdId, resp_id: Option<CmdId>, access: Access) -> Self {
Self {
id,
resp_id,
access,
}
}
}
impl core::fmt::Display for Command {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
#[allow(unused_macros)]
#[macro_export]
macro_rules! commands {
($($cmd:expr $(,)?)*) => {
&[
$($cmd,)*
]
}
}
#[allow(unused_macros)]
#[macro_export]
macro_rules! command_enum {
($en:ty) => {
impl core::convert::TryFrom<$crate::dm::CmdId> for $en {
type Error = $crate::error::Error;
fn try_from(id: $crate::dm::CmdId) -> Result<Self, Self::Error> {
<$en>::from_repr(id).ok_or_else(|| $crate::error::ErrorCode::CommandNotFound.into())
}
}
};
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct CmdDetails {
pub endpoint_id: EndptId,
pub cluster_id: ClusterId,
pub cmd_id: CmdId,
pub fab_idx: u8,
pub wildcard: bool,
pub command_ref: Option<u16>,
pub cluster_status: Cell<u8>,
}
impl CmdDetails {
pub const fn new(
endpoint_id: EndptId,
cluster_id: ClusterId,
cmd_id: CmdId,
fab_idx: u8,
wildcard: bool,
command_ref: Option<u16>,
) -> Self {
Self {
endpoint_id,
cluster_id,
cmd_id,
fab_idx,
wildcard,
command_ref,
cluster_status: Cell::new(0),
}
}
pub fn set_cluster_status(&self, cluster_status: u8) {
self.cluster_status.set(cluster_status);
}
pub fn cluster_status(&self) -> Option<u16> {
match self.cluster_status.get() {
0 => None,
n => Some(n as u16),
}
}
}
impl CmdDetails {
pub const fn reply_path(&self) -> CmdPath {
CmdPath::new(
Some(self.endpoint_id),
Some(self.cluster_id),
Some(self.cmd_id),
)
}
pub fn status(&self, status: IMStatusCode) -> Option<CmdStatus> {
if self.should_report(status) {
Some(CmdStatus::new(
CmdPath::new(
Some(self.endpoint_id),
Some(self.cluster_id),
Some(self.cmd_id),
),
status,
self.cluster_status(),
self.command_ref,
))
} else {
None
}
}
const fn should_report(&self, status: IMStatusCode) -> bool {
!self.wildcard
|| !matches!(
status,
IMStatusCode::UnsupportedEndpoint
| IMStatusCode::UnsupportedCluster
| IMStatusCode::UnsupportedAttribute
| IMStatusCode::UnsupportedCommand
| IMStatusCode::UnsupportedAccess
| IMStatusCode::UnsupportedRead
| IMStatusCode::UnsupportedWrite
)
}
}