use bacnet_types::constructed::{
BACnetDeviceObjectPropertyReference, BACnetDeviceObjectReference,
BACnetObjectPropertyReference, PropertyReference,
};
use bacnet_types::primitives::ObjectIdentifier;
use crate::primitives::{encode_ctx_object_id_to_sink, encode_ctx_unsigned_to_sink};
use crate::sink::{EncodeSink, SinkFull};
use crate::tags::{encode_closing_tag_to_sink, encode_opening_tag_to_sink};
pub fn encode_property_reference_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
reference: &PropertyReference,
) -> Result<usize, SinkFull> {
stage_and_write::<S, 10>(sink, |tail| {
encode_ctx_unsigned_to_sink(tail, 0, u64::from(reference.property_identifier.to_raw()))?;
if let Some(index) = reference.property_array_index {
encode_ctx_unsigned_to_sink(tail, 1, u64::from(index))?;
}
Ok(())
})
}
pub fn encode_object_property_reference_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
reference: &BACnetObjectPropertyReference,
) -> Result<usize, SinkFull> {
stage_and_write::<S, 15>(sink, |tail| {
stage_object_property(
tail,
&reference.object_identifier,
reference.property_identifier,
reference.property_array_index,
)
})
}
pub fn encode_setpoint_reference_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
reference: &BACnetObjectPropertyReference,
) -> Result<usize, SinkFull> {
stage_and_write::<S, 17>(sink, |tail| {
encode_opening_tag_to_sink(tail, 0)?;
stage_object_property(
tail,
&reference.object_identifier,
reference.property_identifier,
reference.property_array_index,
)?;
encode_closing_tag_to_sink(tail, 0)?;
Ok(())
})
}
pub fn encode_device_object_property_reference_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
reference: &BACnetDeviceObjectPropertyReference,
) -> Result<usize, SinkFull> {
stage_and_write::<S, 20>(sink, |tail| {
stage_object_property(
tail,
&reference.object_identifier,
reference.property_identifier,
reference.property_array_index,
)?;
if let Some(device) = &reference.device_identifier {
encode_ctx_object_id_to_sink(tail, 3, device)?;
}
Ok(())
})
}
pub fn encode_device_object_reference_to_sink<S: EncodeSink + ?Sized>(
sink: &mut S,
reference: &BACnetDeviceObjectReference,
) -> Result<usize, SinkFull> {
stage_and_write::<S, 10>(sink, |tail| {
if let Some(device) = &reference.device_identifier {
encode_ctx_object_id_to_sink(tail, 0, device)?;
}
encode_ctx_object_id_to_sink(tail, 1, &reference.object_identifier)?;
Ok(())
})
}
fn stage_object_property(
tail: &mut &mut [u8],
object: &ObjectIdentifier,
property: u32,
index: Option<u32>,
) -> Result<(), SinkFull> {
encode_ctx_object_id_to_sink(tail, 0, object)?;
encode_ctx_unsigned_to_sink(tail, 1, u64::from(property))?;
if let Some(index) = index {
encode_ctx_unsigned_to_sink(tail, 2, u64::from(index))?;
}
Ok(())
}
fn stage_and_write<S: EncodeSink + ?Sized, const N: usize>(
sink: &mut S,
stage: impl FnOnce(&mut &mut [u8]) -> Result<(), SinkFull>,
) -> Result<usize, SinkFull> {
let mut encoded = [0; N];
let mut tail = &mut encoded[..];
stage(&mut tail).expect("each reference fits its bounded staging buffer");
let used = N - tail.len();
if let Some(available) = sink.remaining_capacity()
&& available < used {
return Err(SinkFull {
needed: used,
available,
});
}
sink.write(&encoded[..used])?;
Ok(used)
}