use num::FromPrimitive;
use num_derive::FromPrimitive;
use crate::error::{Error, ErrorCode};
use crate::tlv::{FromTLV, TLVElement, TLVTag, TLVWrite, ToTLV, TLV};
use crate::transport::exchange::MessageMeta;
pub use attr::*;
pub use event::*;
pub use invoke::*;
pub use invoke_builder::*;
pub use status::*;
pub use timed::*;
pub use types::*;
mod attr;
mod event;
mod invoke;
mod invoke_builder;
mod status;
mod timed;
pub(crate) mod types;
pub type IMBuffer = crate::transport::exchange::Buffer;
pub const PROTO_ID_INTERACTION_MODEL: u16 = 0x01;
pub const IM_REVISION: u8 = 13;
pub const IM_REVISION_TAG: u8 = 0xFF;
pub const FABRIC_INDEX_TAG: u8 = 0xFE;
#[derive(FromPrimitive, Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum IMStatusCode {
Success = 0,
Failure = 1,
InvalidSubscription = 0x7D,
UnsupportedAccess = 0x7E,
UnsupportedEndpoint = 0x7F,
InvalidAction = 0x80,
UnsupportedCommand = 0x81,
InvalidCommand = 0x85,
UnsupportedAttribute = 0x86,
ConstraintError = 0x87,
UnsupportedWrite = 0x88,
ResourceExhausted = 0x89,
NotFound = 0x8b,
UnreportableAttribute = 0x8c,
InvalidDataType = 0x8d,
UnsupportedRead = 0x8f,
DataVersionMismatch = 0x92,
Timeout = 0x94,
UnsupportedNode = 0x9b,
Busy = 0x9c,
UnsupportedCluster = 0xc3,
NoUpstreamSubscription = 0xc5,
NeedsTimedInteraction = 0xc6,
UnsupportedEvent = 0xc7,
PathsExhausted = 0xc8,
TimedRequestMisMatch = 0xc9,
FailSafeRequired = 0xca,
InvalidInState = 0xcb,
NoCommandResponse = 0xcc,
DynamicConstraintError = 0xcf,
}
impl From<ErrorCode> for IMStatusCode {
fn from(e: ErrorCode) -> Self {
match e {
ErrorCode::NodeNotFound => IMStatusCode::UnsupportedNode,
ErrorCode::EndpointNotFound => IMStatusCode::UnsupportedEndpoint,
ErrorCode::ClusterNotFound => IMStatusCode::UnsupportedCluster,
ErrorCode::AttributeNotFound => IMStatusCode::UnsupportedAttribute,
ErrorCode::CommandNotFound => IMStatusCode::UnsupportedCommand,
ErrorCode::EventNotFound => IMStatusCode::UnsupportedEvent,
ErrorCode::InvalidAction => IMStatusCode::InvalidAction,
ErrorCode::InvalidCommand => IMStatusCode::InvalidCommand,
ErrorCode::InvalidDataType => IMStatusCode::InvalidDataType,
ErrorCode::UnsupportedAccess => IMStatusCode::UnsupportedAccess,
ErrorCode::Busy => IMStatusCode::Busy,
ErrorCode::DataVersionMismatch => IMStatusCode::DataVersionMismatch,
ErrorCode::ResourceExhausted => IMStatusCode::ResourceExhausted,
ErrorCode::FailSafeRequired => IMStatusCode::FailSafeRequired,
ErrorCode::NeedsTimedInteraction => IMStatusCode::NeedsTimedInteraction,
ErrorCode::ConstraintError => IMStatusCode::ConstraintError,
ErrorCode::DynamicConstraintError => IMStatusCode::DynamicConstraintError,
ErrorCode::NotFound => IMStatusCode::NotFound,
ErrorCode::Failure => IMStatusCode::Failure,
_ => IMStatusCode::Failure,
}
}
}
impl From<Error> for IMStatusCode {
fn from(value: Error) -> Self {
Self::from(value.code())
}
}
impl IMStatusCode {
pub fn to_error_code(self) -> Option<ErrorCode> {
match self {
Self::Success => None,
Self::UnsupportedAccess => Some(ErrorCode::UnsupportedAccess),
Self::InvalidAction => Some(ErrorCode::InvalidAction),
Self::UnsupportedCommand => Some(ErrorCode::CommandNotFound),
Self::InvalidCommand => Some(ErrorCode::InvalidCommand),
Self::UnsupportedAttribute => Some(ErrorCode::AttributeNotFound),
Self::ConstraintError => Some(ErrorCode::ConstraintError),
Self::DynamicConstraintError => Some(ErrorCode::DynamicConstraintError),
Self::ResourceExhausted => Some(ErrorCode::ResourceExhausted),
Self::NotFound => Some(ErrorCode::NotFound),
Self::InvalidDataType => Some(ErrorCode::InvalidDataType),
Self::DataVersionMismatch => Some(ErrorCode::DataVersionMismatch),
Self::Busy => Some(ErrorCode::Busy),
Self::UnsupportedNode => Some(ErrorCode::NodeNotFound),
Self::UnsupportedEndpoint => Some(ErrorCode::EndpointNotFound),
Self::UnsupportedCluster => Some(ErrorCode::ClusterNotFound),
Self::UnsupportedEvent => Some(ErrorCode::EventNotFound),
Self::NeedsTimedInteraction => Some(ErrorCode::NeedsTimedInteraction),
Self::FailSafeRequired => Some(ErrorCode::FailSafeRequired),
_ => Some(ErrorCode::Failure),
}
}
}
impl FromTLV<'_> for IMStatusCode {
fn from_tlv(t: &TLVElement) -> Result<Self, Error> {
FromPrimitive::from_u16(t.u16()?).ok_or_else(|| ErrorCode::Invalid.into())
}
}
impl ToTLV for IMStatusCode {
fn to_tlv<W: TLVWrite>(&self, tag: &TLVTag, mut tw: W) -> Result<(), Error> {
tw.u16(tag, *self as _)
}
fn tlv_iter(&self, tag: TLVTag) -> impl Iterator<Item = Result<TLV<'_>, Error>> {
TLV::u16(tag, *self as _).into_tlv_iter()
}
}
#[derive(FromPrimitive, Debug, Copy, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum OpCode {
Reserved = 0,
StatusResponse = 1,
ReadRequest = 2,
SubscribeRequest = 3,
SubscribeResponse = 4,
ReportData = 5,
WriteRequest = 6,
WriteResponse = 7,
InvokeRequest = 8,
InvokeResponse = 9,
TimedRequest = 10,
}
impl OpCode {
pub const fn meta(&self) -> MessageMeta {
MessageMeta {
proto_id: PROTO_ID_INTERACTION_MODEL,
proto_opcode: *self as u8,
reliable: true,
}
}
pub const fn is_tlv(&self) -> bool {
!matches!(self, Self::Reserved)
}
}
impl From<OpCode> for MessageMeta {
fn from(opcode: OpCode) -> Self {
opcode.meta()
}
}
#[derive(Default, Clone, Debug, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct GenericPath {
pub endpoint: Option<EndptId>,
pub cluster: Option<ClusterId>,
pub leaf: Option<u32>,
}
impl GenericPath {
pub const fn new(
endpoint: Option<EndptId>,
cluster: Option<ClusterId>,
leaf: Option<u32>,
) -> Self {
Self {
endpoint,
cluster,
leaf,
}
}
pub fn not_wildcard(&self) -> Result<(EndptId, ClusterId, u32), Error> {
match *self {
GenericPath {
endpoint: Some(e),
cluster: Some(c),
leaf: Some(l),
} => Ok((e, c, l)),
_ => Err(ErrorCode::Invalid.into()),
}
}
pub const fn is_wildcard(&self) -> bool {
!matches!(
*self,
GenericPath {
endpoint: Some(_),
cluster: Some(_),
leaf: Some(_),
}
)
}
}