use crate::error::{Error, ErrorCode};
use crate::im::{EventFilter, NodeId};
use crate::tlv::{FromTLV, Nullable, TLVArray, TLVElement, ToTLV};
use super::{AttrId, ClusterId, EndptId, EventPath, EventResp, GenericPath, IMStatusCode, Status};
pub use read::*;
pub use read_builder::*;
pub use subscribe::*;
pub use subscribe_builder::*;
pub use write::*;
pub use write_builder::*;
mod read;
mod read_builder;
mod subscribe;
mod subscribe_builder;
mod write;
mod write_builder;
#[derive(Default, Clone, Debug, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(datatype = "list")]
pub struct AttrPath {
pub tag_compression: Option<bool>,
pub node: Option<NodeId>,
pub endpoint: Option<EndptId>,
pub cluster: Option<ClusterId>,
pub attr: Option<AttrId>,
pub list_index: Option<Nullable<u16>>,
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum AttrPathTag {
TagCompression = 0,
Node = 1,
Endpoint = 2,
Cluster = 3,
Attribute = 4,
ListIndex = 5,
}
impl AttrPath {
pub const fn from_gp(path: &GenericPath) -> Self {
Self {
endpoint: path.endpoint,
cluster: path.cluster,
attr: path.leaf,
tag_compression: None,
node: None,
list_index: None,
}
}
pub const fn to_gp(&self) -> GenericPath {
GenericPath::new(self.endpoint, self.cluster, self.attr)
}
pub const fn is_wildcard(&self) -> bool {
self.endpoint.is_none() || self.cluster.is_none() || self.attr.is_none()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct AttrStatus {
pub path: AttrPath,
pub status: Status,
}
impl AttrStatus {
pub const fn new(path: AttrPath, status: IMStatusCode, cluster_status: Option<u16>) -> Self {
Self {
path,
status: Status::new(status, cluster_status),
}
}
pub const fn from_gp(
path: &GenericPath,
status: IMStatusCode,
cluster_status: Option<u16>,
) -> Self {
Self::new(AttrPath::from_gp(path), status, cluster_status)
}
}
#[derive(Debug, Clone, PartialEq, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(lifetime = "'a")]
pub struct AttrData<'a> {
pub data_ver: Option<u32>,
pub path: AttrPath,
pub data: TLVElement<'a>,
}
impl<'a> AttrData<'a> {
pub const fn new(data_ver: Option<u32>, path: AttrPath, data: TLVElement<'a>) -> Self {
Self {
data_ver,
path,
data,
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum AttrDataTag {
DataVer = 0,
Path = 1,
Data = 2,
}
#[derive(Clone, FromTLV, ToTLV, PartialEq, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(lifetime = "'a")]
pub enum AttrResp<'a> {
Status(AttrStatus),
Data(AttrData<'a>),
}
impl<'a> From<AttrData<'a>> for AttrResp<'a> {
fn from(value: AttrData<'a>) -> Self {
Self::Data(value)
}
}
impl From<AttrStatus> for AttrResp<'_> {
fn from(value: AttrStatus) -> Self {
Self::Status(value)
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum AttrRespTag {
Status = 0,
Data = 1,
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[tlvargs(datatype = "list")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct ClusterPath {
pub node: Option<u64>,
pub endpoint: EndptId,
pub cluster: ClusterId,
}
#[derive(Default, Debug, Clone, PartialEq, Eq, Hash, FromTLV, ToTLV)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DataVersionFilter {
pub path: ClusterPath,
pub data_ver: u32,
}
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum ReportDataReq<'a> {
Read(&'a ReadReq<'a>),
Subscribe(&'a SubscribeReq<'a>),
SubscribeReport(&'a SubscribeReq<'a>),
}
impl<'a> ReportDataReq<'a> {
pub fn attr_requests(&self) -> Result<Option<TLVArray<'a, AttrPath>>, Error> {
match self {
Self::Read(req) => req.attr_requests(),
Self::Subscribe(req) | Self::SubscribeReport(req) => req.attr_requests(),
}
}
pub fn event_requests(&self) -> Result<Option<TLVArray<'a, EventPath>>, Error> {
match self {
Self::Read(req) => req.event_requests(),
Self::Subscribe(req) | Self::SubscribeReport(req) => req.event_requests(),
}
}
pub fn dataver_filters(&self) -> Result<Option<TLVArray<'_, DataVersionFilter>>, Error> {
match self {
Self::Read(req) => req.dataver_filters(),
Self::Subscribe(req) => req.dataver_filters(),
Self::SubscribeReport(_) => Ok(None),
}
}
pub fn event_filters(&self) -> Result<Option<TLVArray<'_, EventFilter>>, Error> {
match self {
Self::Read(req) => req.event_filters(),
Self::Subscribe(req) | Self::SubscribeReport(req) => req.event_filters(),
}
}
pub fn fabric_filtered(&self) -> Result<bool, Error> {
match self {
Self::Read(req) => req.fabric_filtered(),
Self::Subscribe(req) | Self::SubscribeReport(req) => req.fabric_filtered(),
}
}
}
#[derive(FromTLV, ToTLV, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[tlvargs(lifetime = "'a")]
pub struct ReportDataResp<'a> {
pub subscription_id: Option<u32>,
pub attr_reports: Option<TLVArray<'a, AttrResp<'a>>>,
pub event_reports: Option<TLVArray<'a, EventResp<'a>>>,
pub more_chunks: Option<bool>,
pub suppress_response: Option<bool>,
#[tagval(crate::im::encoding::IM_REVISION_TAG)]
pub interaction_model_revision: Option<u8>,
}
impl<'a> ReportDataResp<'a> {
pub fn attrs<T>(
&self,
cluster: ClusterId,
attr: AttrId,
) -> impl Iterator<Item = (EndptId, Result<T, Error>)> + use<'_, 'a, T>
where
T: FromTLV<'a> + 'a,
{
self.attr_reports
.as_ref()
.into_iter()
.flat_map(|arr| arr.iter())
.filter_map(move |resp| filter_attr_resp::<T>(resp.ok()?, cluster, attr))
}
}
fn filter_attr_resp<'a, T>(
resp: AttrResp<'a>,
cluster: ClusterId,
attr: AttrId,
) -> Option<(EndptId, Result<T, Error>)>
where
T: FromTLV<'a>,
{
match resp {
AttrResp::Data(data) => {
if data.path.cluster != Some(cluster) || data.path.attr != Some(attr) {
return None;
}
let endpoint = data.path.endpoint?;
Some((endpoint, T::from_tlv(&data.data)))
}
AttrResp::Status(s) => {
if s.path.cluster != Some(cluster) || s.path.attr != Some(attr) {
return None;
}
let endpoint = s.path.endpoint?;
let err: Error = s
.status
.status
.to_error_code()
.unwrap_or(ErrorCode::Failure)
.into();
Some((endpoint, Err(err)))
}
}
}
#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum ReportDataRespTag {
SubscriptionId = 0,
AttributeReports = 1,
EventReports = 2,
MoreChunkedMsgs = 3,
SupressResponse = 4,
}