use crate::layer::ReceivedApdu;
use bacnet_encoding::npdu::{encode_npdu, Npdu, NpduAddress};
use bacnet_transport::port::{DirectResponse, TransportProvenance};
use bacnet_types::{enums::NetworkPriority, error::Error};
use bytes::{Bytes, BytesMut};
#[derive(Clone, Debug)]
pub struct ResponseRoute {
provenance: TransportProvenance,
direct: Option<DirectResponse>,
}
impl ResponseRoute {
pub fn new(provenance: TransportProvenance, direct: Option<DirectResponse>) -> Self {
Self { provenance, direct }
}
pub fn unverified() -> Self {
Self::new(TransportProvenance::unverified(), None)
}
pub fn provenance(&self) -> TransportProvenance {
self.provenance
}
pub fn max_apdu_length(
&self,
apdu_limit: u16,
destination: Option<&NpduAddress>,
) -> Result<u16, Error> {
let Some(direct) = self.direct()? else {
return Ok(apdu_limit);
};
let header = encode_response_npdu(&[], destination, false, NetworkPriority::NORMAL)?;
let available = direct.max_npdu_length().saturating_sub(header.len());
Ok(usize::from(apdu_limit).min(available) as u16)
}
pub(crate) fn direct(&self) -> Result<Option<&DirectResponse>, bacnet_types::error::Error> {
match (self.provenance.direct_sc_identity(), self.direct.as_ref()) {
(Some(identity), Some(route)) if identity == route.identity() => Ok(Some(route)),
(None, None) => Ok(None),
_ => Err(bacnet_types::error::Error::Transport(std::io::Error::new(
std::io::ErrorKind::PermissionDenied,
"missing or mismatched original direct response capability",
))),
}
}
}
impl ReceivedApdu {
pub fn response_route(&self) -> ResponseRoute {
ResponseRoute::new(self.provenance, self.direct_response.clone())
}
}
pub(crate) fn encode_response_npdu(
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
check_response_destination(destination)?;
encode_checked_response_npdu(apdu, destination, expecting_reply, priority)
}
pub(crate) fn check_response_destination(destination: Option<&NpduAddress>) -> Result<(), Error> {
match destination {
Some(destination) => crate::layer::check_destination(
destination.network,
"pass no destination (no DNET) for a local peer",
),
None => Ok(()),
}
}
pub(crate) fn encode_checked_response_npdu(
apdu: &[u8],
destination: Option<&NpduAddress>,
expecting_reply: bool,
priority: NetworkPriority,
) -> Result<BytesMut, Error> {
let npdu = Npdu {
destination: destination.cloned(),
expecting_reply,
priority,
payload: Bytes::copy_from_slice(apdu),
..Npdu::default()
};
let mut encoded = BytesMut::new();
encode_npdu(&mut encoded, &npdu)?;
Ok(encoded)
}