bacnet_server/server/confirmed_answer.rs
1//! The answer a device gives a confirmed request this server sent: a
2//! confirmed notification, a WriteProperty a run makes in another device, or
3//! the ReadProperty a Channel sends there to learn a member's datatype
4//! (#1342).
5//!
6//! An answer that turns the request down keeps what it said (#1323): the
7//! Error PDU's class and code, or the Reject or Abort reason. Notification
8//! senders only ask whether the request was taken; a Channel writing a member
9//! in another device needs the code too, since a NULL refused as an invalid
10//! datatype still counts as written (Clause 12.53.7).
11
12use bacnet_types::enums::{AbortReason, ErrorClass, ErrorCode, RejectReason};
13use bacnet_types::error::Error;
14use bytes::Bytes;
15
16/// Result of a confirmed request from the receiving device's side.
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum CovAckResult {
19 /// SimpleAck received: the device took the request.
20 Ack,
21 /// An unsegmented ComplexAck received, with its service data: the device
22 /// answered a read.
23 Data(Bytes),
24 /// Error, Reject or Abort received: the device turned the request down.
25 Error(Refusal),
26}
27
28/// How a device turned down a confirmed request.
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum Refusal {
31 /// An Error PDU.
32 Error {
33 /// Its error class.
34 class: ErrorClass,
35 /// Its error code.
36 code: ErrorCode,
37 },
38 /// A Reject PDU, with its reason.
39 Reject(RejectReason),
40 /// An Abort PDU, with its reason.
41 Abort(AbortReason),
42}
43
44/// The error a client's WriteProperty answered this way reports:
45/// [`Error::Protocol`], [`Error::Reject`] or [`Error::Abort`]. A `Refusal`
46/// keeps only an Error's class and code, so it never becomes
47/// [`Error::Structured`], which a client reports for the services whose
48/// Error carries more.
49impl From<Refusal> for Error {
50 fn from(refusal: Refusal) -> Self {
51 match refusal {
52 Refusal::Error { class, code } => Self::Protocol { class, code },
53 Refusal::Reject(reason) => Self::Reject { reason },
54 Refusal::Abort(reason) => Self::Abort { reason },
55 }
56 }
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn refusal_converts_to_the_error_a_client_request_reports() {
65 let error = Refusal::Error {
66 class: ErrorClass::PROPERTY,
67 code: ErrorCode::INVALID_DATA_TYPE,
68 };
69 assert!(matches!(
70 Error::from(error),
71 Error::Protocol {
72 class: ErrorClass::PROPERTY,
73 code: ErrorCode::INVALID_DATA_TYPE
74 }
75 ));
76 let reject = Refusal::Reject(RejectReason::INVALID_PARAMETER_DATA_TYPE);
77 assert!(matches!(
78 Error::from(reject),
79 Error::Reject {
80 reason: RejectReason::INVALID_PARAMETER_DATA_TYPE
81 }
82 ));
83 let abort = Refusal::Abort(AbortReason::SEGMENTATION_NOT_SUPPORTED);
84 assert!(matches!(
85 Error::from(abort),
86 Error::Abort {
87 reason: AbortReason::SEGMENTATION_NOT_SUPPORTED
88 }
89 ));
90 }
91}