opcua/types/
extension_object.rs1use std::{
8 error::Error,
9 fmt,
10 io::{Cursor, Read, Write},
11};
12
13use super::{
14 byte_string::ByteString, encoding::*, node_id::NodeId, node_ids::ObjectId,
15 status_codes::StatusCode, string::XmlElement,
16};
17
18#[derive(Debug)]
19pub struct ExtensionObjectError;
20
21impl fmt::Display for ExtensionObjectError {
22 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
23 write!(f, "ExtensionObjectError")
24 }
25}
26
27impl Error for ExtensionObjectError {}
28
29#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
31pub enum ExtensionObjectEncoding {
32 None,
34 ByteString(ByteString),
36 XmlElement(XmlElement),
38}
39
40#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
42#[serde(rename_all = "PascalCase")]
43pub struct ExtensionObject {
44 pub node_id: NodeId,
45 pub body: ExtensionObjectEncoding,
46}
47
48impl Default for ExtensionObject {
49 fn default() -> Self {
50 Self::null()
51 }
52}
53
54impl BinaryEncoder<ExtensionObject> for ExtensionObject {
55 fn byte_len(&self) -> usize {
56 let mut size = self.node_id.byte_len();
57 size += match self.body {
58 ExtensionObjectEncoding::None => 1,
59 ExtensionObjectEncoding::ByteString(ref value) => {
60 1 + value.byte_len()
62 }
63 ExtensionObjectEncoding::XmlElement(ref value) => {
64 1 + value.byte_len()
66 }
67 };
68 size
69 }
70
71 fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
72 let mut size = 0;
73 size += self.node_id.encode(stream)?;
74 match self.body {
75 ExtensionObjectEncoding::None => {
76 size += write_u8(stream, 0x0)?;
77 }
78 ExtensionObjectEncoding::ByteString(ref value) => {
79 size += write_u8(stream, 0x1)?;
81 size += value.encode(stream)?;
82 }
83 ExtensionObjectEncoding::XmlElement(ref value) => {
84 size += write_u8(stream, 0x2)?;
86 size += value.encode(stream)?;
87 }
88 }
89 assert_eq!(size, self.byte_len());
90 Ok(size)
91 }
92
93 fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
94 let _depth_lock = decoding_options.depth_lock()?;
96 let node_id = NodeId::decode(stream, decoding_options)?;
97 let encoding_type = u8::decode(stream, decoding_options)?;
98 let body = match encoding_type {
99 0x0 => ExtensionObjectEncoding::None,
100 0x1 => {
101 ExtensionObjectEncoding::ByteString(ByteString::decode(stream, decoding_options)?)
102 }
103 0x2 => {
104 ExtensionObjectEncoding::XmlElement(XmlElement::decode(stream, decoding_options)?)
105 }
106 _ => {
107 error!("Invalid encoding type {} in stream", encoding_type);
108 return Err(StatusCode::BadDecodingError);
109 }
110 };
111 Ok(ExtensionObject { node_id, body })
112 }
113}
114
115impl ExtensionObject {
116 pub fn null() -> ExtensionObject {
118 ExtensionObject {
119 node_id: NodeId::null(),
120 body: ExtensionObjectEncoding::None,
121 }
122 }
123
124 pub fn is_null(&self) -> bool {
126 self.node_id.is_null()
127 }
128
129 pub fn is_empty(&self) -> bool {
131 self.is_null() || matches!(self.body, ExtensionObjectEncoding::None)
132 }
133
134 pub fn object_id(&self) -> Result<ObjectId, ExtensionObjectError> {
137 self.node_id
138 .as_object_id()
139 .map_err(|_| ExtensionObjectError)
140 }
141
142 pub fn from_encodable<N, T>(node_id: N, encodable: &T) -> ExtensionObject
145 where
146 N: Into<NodeId>,
147 T: BinaryEncoder<T>,
148 {
149 let mut stream = Cursor::new(vec![0u8; encodable.byte_len()]);
151 let _ = encodable.encode(&mut stream);
152 ExtensionObject {
153 node_id: node_id.into(),
154 body: ExtensionObjectEncoding::ByteString(ByteString::from(stream.into_inner())),
155 }
156 }
157
158 pub fn decode_inner<T>(&self, decoding_options: &DecodingOptions) -> EncodingResult<T>
162 where
163 T: BinaryEncoder<T>,
164 {
165 match self.body {
166 ExtensionObjectEncoding::ByteString(ref byte_string) => {
167 if let Some(ref value) = byte_string.value {
168 let mut stream = Cursor::new(value);
170 T::decode(&mut stream, decoding_options)
171 } else {
172 Err(StatusCode::BadDecodingError)
173 }
174 }
175 _ => {
176 error!("decode_inner called on an unsupported ExtensionObject type");
177 Err(StatusCode::BadDecodingError)
178 }
179 }
180 }
181}