use core::fmt::Debug;
use crate::dm::{ArrayAttributeRead, Cluster, Dataver, Endpoint, EndptId, Metadata, ReadContext};
use crate::error::{Error, ErrorCode};
use crate::tlv::{TLVBuilderParent, ToTLVArrayBuilder, ToTLVBuilder};
use crate::utils::sync::DynBase;
use crate::with;
pub use crate::dm::clusters::decl::descriptor::*;
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct StandardPartsMatcher;
impl DynBase for StandardPartsMatcher {}
impl PartsMatcher for StandardPartsMatcher {
fn matches(&self, our_endpoint: EndptId, endpoint: EndptId) -> bool {
our_endpoint == 0 && endpoint != our_endpoint
}
}
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
struct AggregatorPartsMatcher;
impl DynBase for AggregatorPartsMatcher {}
impl PartsMatcher for AggregatorPartsMatcher {
fn matches(&self, our_endpoint: EndptId, endpoint: EndptId) -> bool {
endpoint != our_endpoint && endpoint != 0
}
}
pub trait PartsMatcher: DynBase + Debug {
fn matches(&self, our_endpoint: EndptId, endpoint: EndptId) -> bool;
}
impl<T> PartsMatcher for &T
where
T: PartsMatcher,
{
fn matches(&self, our_endpoint: EndptId, endpoint: EndptId) -> bool {
(**self).matches(our_endpoint, endpoint)
}
}
#[derive(Clone, Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct DescHandler<'a> {
dataver: Dataver,
matcher: &'a dyn PartsMatcher,
}
impl DescHandler<'static> {
pub const fn new(dataver: Dataver) -> Self {
Self::new_matching(dataver, &StandardPartsMatcher)
}
pub const fn new_aggregator(dataver: Dataver) -> Self {
Self::new_matching(dataver, &AggregatorPartsMatcher)
}
}
impl<'a> DescHandler<'a> {
pub const fn new_matching(dataver: Dataver, matcher: &'a dyn PartsMatcher) -> DescHandler<'a> {
Self { dataver, matcher }
}
pub const fn adapt(self) -> HandlerAdaptor<Self> {
HandlerAdaptor(self)
}
fn with_endpoint<F, R>(ctx: impl ReadContext, f: F) -> Result<R, Error>
where
F: FnOnce(&Endpoint) -> Result<R, Error>,
{
let metadata = ctx.metadata();
metadata.access(|node| {
let endpoint = node
.endpoint(ctx.attr().endpoint_id)
.ok_or_else(|| Error::new(ErrorCode::EndpointNotFound))?;
f(endpoint)
})
}
}
impl ClusterHandler for DescHandler<'_> {
const CLUSTER: Cluster<'static> = FULL_CLUSTER.with_attrs(with!(required)).with_cmds(with!());
fn dataver(&self) -> u32 {
self.dataver.get()
}
fn dataver_changed(&self) {
self.dataver.changed();
}
fn device_type_list<P: TLVBuilderParent>(
&self,
ctx: impl ReadContext,
builder: ArrayAttributeRead<DeviceTypeStructArrayBuilder<P>, DeviceTypeStructBuilder<P>>,
) -> Result<P, Error> {
Self::with_endpoint(ctx, |endpoint| match builder {
ArrayAttributeRead::ReadAll(mut builder) => {
for dev_type in endpoint.device_types {
builder = builder
.push()?
.device_type(dev_type.dtype as _)?
.revision(dev_type.drev)?
.end()?;
}
builder.end()
}
ArrayAttributeRead::ReadOne(index, builder) => {
let Some(dev_type) = endpoint.device_types.get(index as usize) else {
return Err(ErrorCode::ConstraintError.into());
};
builder
.device_type(dev_type.dtype as _)?
.revision(dev_type.drev)?
.end()
}
ArrayAttributeRead::ReadNone(builder) => builder.end(),
})
}
fn server_list<P: TLVBuilderParent>(
&self,
ctx: impl ReadContext,
builder: ArrayAttributeRead<ToTLVArrayBuilder<P, u32>, ToTLVBuilder<P, u32>>,
) -> Result<P, Error> {
Self::with_endpoint(ctx, |endpoint| match builder {
ArrayAttributeRead::ReadAll(mut builder) => {
for cluster in endpoint.clusters {
builder = builder.push(&cluster.id)?;
}
builder.end()
}
ArrayAttributeRead::ReadOne(index, builder) => {
let Some(cluster) = endpoint.clusters.get(index as usize) else {
return Err(ErrorCode::ConstraintError.into());
};
builder.set(&cluster.id)
}
ArrayAttributeRead::ReadNone(builder) => builder.end(),
})
}
fn client_list<P: TLVBuilderParent>(
&self,
ctx: impl ReadContext,
builder: ArrayAttributeRead<ToTLVArrayBuilder<P, u32>, ToTLVBuilder<P, u32>>,
) -> Result<P, Error> {
Self::with_endpoint(ctx, |endpoint| match builder {
ArrayAttributeRead::ReadAll(mut builder) => {
for client_id in endpoint.client_clusters {
builder = builder.push(client_id)?;
}
builder.end()
}
ArrayAttributeRead::ReadOne(index, builder) => {
let Some(client_id) = endpoint.client_clusters.get(index as usize) else {
return Err(ErrorCode::ConstraintError.into());
};
builder.set(client_id)
}
ArrayAttributeRead::ReadNone(builder) => builder.end(),
})
}
fn parts_list<P: TLVBuilderParent>(
&self,
ctx: impl ReadContext,
builder: ArrayAttributeRead<ToTLVArrayBuilder<P, u16>, ToTLVBuilder<P, u16>>,
) -> Result<P, Error> {
let metadata = ctx.metadata();
metadata.access(|node| {
let mut ep_ids = node
.endpoints
.iter()
.map(|e| e.id)
.filter(|e| self.matcher.matches(ctx.attr().endpoint_id, *e));
match builder {
ArrayAttributeRead::ReadAll(mut builder) => {
for id in ep_ids {
builder = builder.push(&id)?;
}
builder.end()
}
ArrayAttributeRead::ReadOne(index, builder) => {
let Some(ep_id) = ep_ids.nth(index as usize) else {
return Err(ErrorCode::ConstraintError.into());
};
builder.set(&ep_id)
}
ArrayAttributeRead::ReadNone(builder) => builder.end(),
}
})
}
}