use core::fmt;
use crate::acl::Accessor;
use crate::dm::{Cluster, Endpoint};
use crate::im::encoding::{AttrPath, EventPath, GenericPath, IMStatusCode};
use crate::utils::init::{init, Init};
use crate::utils::storage::Vec;
use super::{EndptId, NodeId};
#[derive(Debug, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct Node<'a> {
pub endpoints: &'a [Endpoint<'a>],
}
impl<'a> Node<'a> {
pub const fn new(endpoints: &'a [Endpoint<'a>]) -> Self {
Self { endpoints }
}
pub fn endpoint(&self, id: EndptId) -> Option<&Endpoint<'a>> {
self.endpoints.iter().find(|endpoint| endpoint.id == id)
}
pub(crate) fn validate_attr_path(
&self,
path: &AttrPath,
timed: bool,
write: bool,
accessor: &Accessor<'_>,
) -> Result<(), IMStatusCode> {
if let Some(node_id) = path.node {
self.validate_node_id(node_id, accessor)?;
}
let gp = path.to_gp();
let Some((endpoint, cluster, attr_id)) = self.validate_cluster_path(&gp)? else {
return Ok(());
};
let Some(attr) = cluster.attribute(attr_id) else {
return Err(IMStatusCode::UnsupportedAttribute);
};
cluster.check_attr_access(accessor, timed, gp, endpoint.device_types, write, attr.id)
}
pub(crate) fn validate_event_path(
&self,
path: &EventPath,
accessor: &Accessor<'_>,
) -> Result<(), IMStatusCode> {
if let Some(node_id) = path.node {
self.validate_node_id(node_id, accessor)?;
}
let gp = path.to_gp();
let Some((endpoint, cluster, event_id)) = self.validate_cluster_path(&gp)? else {
return Ok(());
};
let Some(event) = cluster.event(event_id) else {
return Err(IMStatusCode::UnsupportedEvent);
};
cluster.check_event_access(accessor, gp, endpoint.device_types, event.id)
}
fn validate_cluster_path(
&self,
path: &GenericPath,
) -> Result<Option<(&Endpoint<'_>, &Cluster<'_>, u32)>, IMStatusCode> {
let Some(endpoint_id) = path.endpoint else {
return Ok(None);
};
let Some(endpoint) = self.endpoint(endpoint_id) else {
return Err(IMStatusCode::UnsupportedEndpoint);
};
let Some(cluster_id) = path.cluster else {
return Ok(None);
};
let Some(cluster) = endpoint.cluster(cluster_id) else {
return Err(IMStatusCode::UnsupportedCluster);
};
let Some(leaf_id) = path.leaf else {
return Ok(None);
};
Ok(Some((endpoint, cluster, leaf_id)))
}
fn validate_node_id(
&self,
node_id: NodeId,
accessor: &Accessor<'_>,
) -> Result<(), IMStatusCode> {
let Some(accessor_node_id) = accessor.node_id() else {
return Err(IMStatusCode::UnsupportedNode);
};
if node_id != accessor_node_id {
return Err(IMStatusCode::UnsupportedNode);
}
Ok(())
}
pub(crate) fn has_accessible_attr(&self, path: &AttrPath, accessor: &Accessor<'_>) -> bool {
for endpoint in self.endpoints.iter() {
if let Some(ep_id) = path.endpoint {
if endpoint.id != ep_id {
continue;
}
}
for cluster in endpoint.clusters.iter() {
if let Some(cluster_id) = path.cluster {
if cluster.id != cluster_id {
continue;
}
}
for attr in cluster.attributes.iter() {
if let Some(attr_id) = path.attr {
if attr.id != attr_id {
continue;
}
}
let gp = GenericPath::new(Some(endpoint.id), Some(cluster.id), Some(attr.id));
if cluster
.check_attr_access(
accessor,
false,
gp,
endpoint.device_types,
false,
attr.id,
)
.is_ok()
{
return true;
}
}
}
}
false
}
}
impl core::fmt::Display for Node<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "node:")?;
for (index, endpoint) in self.endpoints.iter().enumerate() {
writeln!(f, "endpoint {}: {}", index, endpoint)?;
}
write!(f, "")
}
}
pub struct DynamicNode<'a, const N: usize> {
endpoints: Vec<Endpoint<'a>, N>,
}
impl<'a, const N: usize> DynamicNode<'a, N> {
pub const fn new() -> Self {
Self {
endpoints: Vec::new(),
}
}
pub fn init() -> impl Init<Self> {
init!(Self {
endpoints <- Vec::init(),
})
}
pub fn node(&self) -> Node<'_> {
Node {
endpoints: &self.endpoints,
}
}
pub fn add(&mut self, endpoint: Endpoint<'a>) -> Result<(), Endpoint<'a>> {
match self.endpoints.iter().position(|ep| ep.id >= endpoint.id) {
Some(i) if self.endpoints[i].id == endpoint.id => Err(endpoint),
Some(i) => self.endpoints.insert(i, endpoint),
None => self.endpoints.push(endpoint),
}
}
pub fn remove(&mut self, endpoint_id: u16) -> Option<Endpoint<'a>> {
let index = self.endpoints.iter().position(|ep| ep.id == endpoint_id)?;
Some(self.endpoints.remove(index))
}
}
impl<const N: usize> core::fmt::Display for DynamicNode<'_, N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.node().fmt(f)
}
}
impl<'a, const N: usize> Default for DynamicNode<'a, N> {
fn default() -> Self {
Self::new()
}
}