opcua_types/
status_code.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the hand implemented part of the StatusCode type. The other file, `status_codes.rs` contains
6//! the machine generated part.
7
8use std::{
9    error::Error,
10    fmt,
11    fmt::Formatter,
12    io::{self, Read, Write},
13};
14
15use serde::{
16    de::{self, Visitor},
17    Deserialize, Deserializer, Serialize, Serializer,
18};
19
20pub use crate::{encoding::*, status_codes::StatusCode};
21
22// The bitflags! macro implements Debug for StatusCode but it fouls the display because status
23// codes are a combination of bits and unique values.
24
25impl fmt::Display for StatusCode {
26    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
27        // Displays the StatusCode as it's name, or its name+bitflags
28        let bits = self.bitflags();
29        if bits.is_empty() {
30            write!(f, "{}", self.name())
31        } else {
32            write!(f, "{}+{:?}", self.name(), bits)
33        }
34    }
35}
36
37impl BinaryEncoder<StatusCode> for StatusCode {
38    fn byte_len(&self) -> usize {
39        4
40    }
41
42    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
43        write_u32(stream, self.bits())
44    }
45
46    fn decode<S: Read>(stream: &mut S, _: &DecodingOptions) -> EncodingResult<Self> {
47        Ok(StatusCode::from_bits_truncate(read_u32(stream)?))
48    }
49}
50
51impl Error for StatusCode {}
52
53impl StatusCode {
54    /// Returns the bit flags of the status code, i.e. it masks out the actual status code value
55    pub fn bitflags(&self) -> StatusCode {
56        *self & StatusCode::BIT_MASK
57    }
58
59    /// Returns the status only, i.e. it masks out any bit flags that come with the status code
60    pub fn status(&self) -> StatusCode {
61        *self & StatusCode::STATUS_MASK
62    }
63
64    /// Tests if the status code is bad
65    pub fn is_bad(&self) -> bool {
66        self.contains(StatusCode::IS_ERROR)
67    }
68
69    /// Tests if the status code is uncertain
70    pub fn is_uncertain(&self) -> bool {
71        self.contains(StatusCode::IS_UNCERTAIN)
72    }
73
74    /// Tests if the status code is good (i.e. not bad or uncertain)
75    pub fn is_good(&self) -> bool {
76        !self.is_bad() && !self.is_uncertain()
77    }
78}
79
80// It would be very nice to be able to override the default implementation in bitflags! macro
81// of this fmt::Debug because it breaks on StatusCode
82/*
83impl fmt::Debug for StatusCode {
84    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
85        // Displays the StatusCode as it's name, or its name+bitflags
86        let bits = self.bitflags();
87        if bits.is_empty() {
88            write!(f, "{}", self.name())
89        } else {
90            write!(f, "{}+{:?}", self.name(), bits)
91        }
92    }
93}
94*/
95
96// Serialize / Deserialize are manually implemented because bitflags! doesn't do it.
97
98impl From<StatusCode> for io::Error {
99    fn from(e: StatusCode) -> io::Error {
100        io::Error::new(io::ErrorKind::Other, format!("StatusCode {}", e))
101    }
102}
103
104impl Serialize for StatusCode {
105    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
106    where
107        S: Serializer,
108    {
109        serializer.serialize_u32(self.bits())
110    }
111}
112
113struct StatusCodeVisitor;
114
115impl<'de> Visitor<'de> for StatusCodeVisitor {
116    type Value = u32;
117
118    fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
119        formatter.write_str("an unsigned 32-bit integer")
120    }
121
122    fn visit_u32<E>(self, value: u32) -> Result<Self::Value, E>
123    where
124        E: de::Error,
125    {
126        Ok(value)
127    }
128}
129
130impl<'de> Deserialize<'de> for StatusCode {
131    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
132    where
133        D: Deserializer<'de>,
134    {
135        Ok(StatusCode::from_bits_truncate(
136            deserializer.deserialize_u32(StatusCodeVisitor)?,
137        ))
138    }
139}
140
141#[test]
142fn status_code() {
143    assert!(StatusCode::Good.is_good());
144    assert!(!StatusCode::Good.is_bad());
145    assert!(!StatusCode::Good.is_uncertain());
146
147    assert!(StatusCode::UncertainLastUsableValue.is_uncertain());
148    assert!(!StatusCode::UncertainLastUsableValue.is_bad());
149    assert!(!StatusCode::UncertainLastUsableValue.is_good());
150
151    assert!(StatusCode::BadDecodingError.is_bad());
152    assert!(!StatusCode::BadDecodingError.is_uncertain());
153    assert!(!StatusCode::BadDecodingError.is_good());
154
155    assert_eq!(
156        (StatusCode::BadDecodingError | StatusCode::HISTORICAL_CALCULATED).status(),
157        StatusCode::BadDecodingError
158    );
159    assert_eq!(
160        (StatusCode::BadDecodingError | StatusCode::HISTORICAL_CALCULATED).bitflags(),
161        StatusCode::HISTORICAL_CALCULATED
162    );
163}