Skip to main content

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 {
53                class: class.to_raw().into(),
54                code: code.to_raw().into(),
55            },
56            Refusal::Reject(reason) => Self::Reject {
57                reason: reason.to_raw(),
58            },
59            Refusal::Abort(reason) => Self::Abort {
60                reason: reason.to_raw(),
61            },
62        }
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    fn refusal_converts_to_the_error_a_client_request_reports() {
72        let error = Refusal::Error {
73            class: ErrorClass::PROPERTY,
74            code: ErrorCode::INVALID_DATA_TYPE,
75        };
76        assert!(matches!(
77            Error::from(error),
78            Error::Protocol { class: 2, code: 9 }
79        ));
80        let reject = Refusal::Reject(RejectReason::INVALID_PARAMETER_DATA_TYPE);
81        assert!(matches!(Error::from(reject), Error::Reject { reason: 3 }));
82        let abort = Refusal::Abort(AbortReason::SEGMENTATION_NOT_SUPPORTED);
83        assert!(matches!(Error::from(abort), Error::Abort { reason: 4 }));
84    }
85}