opcua_types/
extension_object.rs

1// OPCUA for Rust
2// SPDX-License-Identifier: MPL-2.0
3// Copyright (C) 2017-2022 Adam Lock
4
5//! Contains the implementation of `ExtensionObject`.
6
7use std::{
8    error::Error,
9    fmt,
10    io::{Cursor, Read, Write},
11};
12
13use crate::{
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/// Enumeration that holds the kinds of encoding that an ExtensionObject data may be encoded with.
30#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
31pub enum ExtensionObjectEncoding {
32    /// For an extension object with nothing encoded with it
33    None,
34    /// For an extension object with data encoded in a ByteString
35    ByteString(ByteString),
36    /// For an extension object with data encoded in an XML string
37    XmlElement(XmlElement),
38}
39
40/// An extension object holds a serialized object identified by its node id.
41#[derive(PartialEq, Debug, Clone, Serialize, Deserialize)]
42pub struct ExtensionObject {
43    pub node_id: NodeId,
44    pub body: ExtensionObjectEncoding,
45}
46
47impl BinaryEncoder<ExtensionObject> for ExtensionObject {
48    fn byte_len(&self) -> usize {
49        let mut size = self.node_id.byte_len();
50        size += match self.body {
51            ExtensionObjectEncoding::None => 1,
52            ExtensionObjectEncoding::ByteString(ref value) => {
53                // Encoding mask + data
54                1 + value.byte_len()
55            }
56            ExtensionObjectEncoding::XmlElement(ref value) => {
57                // Encoding mask + data
58                1 + value.byte_len()
59            }
60        };
61        size
62    }
63
64    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
65        let mut size = 0;
66        size += self.node_id.encode(stream)?;
67        match self.body {
68            ExtensionObjectEncoding::None => {
69                size += write_u8(stream, 0x0)?;
70            }
71            ExtensionObjectEncoding::ByteString(ref value) => {
72                // Encoding mask + data
73                size += write_u8(stream, 0x1)?;
74                size += value.encode(stream)?;
75            }
76            ExtensionObjectEncoding::XmlElement(ref value) => {
77                // Encoding mask + data
78                size += write_u8(stream, 0x2)?;
79                size += value.encode(stream)?;
80            }
81        }
82        assert_eq!(size, self.byte_len());
83        Ok(size)
84    }
85
86    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
87        let node_id = NodeId::decode(stream, decoding_options)?;
88        let encoding_type = u8::decode(stream, decoding_options)?;
89        let body = match encoding_type {
90            0x0 => ExtensionObjectEncoding::None,
91            0x1 => {
92                ExtensionObjectEncoding::ByteString(ByteString::decode(stream, decoding_options)?)
93            }
94            0x2 => {
95                ExtensionObjectEncoding::XmlElement(XmlElement::decode(stream, decoding_options)?)
96            }
97            _ => {
98                error!("Invalid encoding type {} in stream", encoding_type);
99                return Err(StatusCode::BadDecodingError);
100            }
101        };
102        Ok(ExtensionObject { node_id, body })
103    }
104}
105
106impl ExtensionObject {
107    /// Creates a null extension object, i.e. one with no value or payload
108    pub fn null() -> ExtensionObject {
109        ExtensionObject {
110            node_id: NodeId::null(),
111            body: ExtensionObjectEncoding::None,
112        }
113    }
114
115    /// Tests for null node id.
116    pub fn is_null(&self) -> bool {
117        self.node_id.is_null()
118    }
119
120    /// Tests for empty body.
121    pub fn is_empty(&self) -> bool {
122        self.is_null() || matches!(self.body, ExtensionObjectEncoding::None)
123    }
124
125    /// Returns the object id of the thing this extension object contains, assuming the
126    /// object id can be recognised from the node id.
127    pub fn object_id(&self) -> Result<ObjectId, ExtensionObjectError> {
128        self.node_id
129            .as_object_id()
130            .map_err(|_| ExtensionObjectError)
131    }
132
133    /// Creates an extension object with the specified node id and the encodable object as its payload.
134    /// The body is set to a byte string containing the encoded struct.
135    pub fn from_encodable<N, T>(node_id: N, encodable: &T) -> ExtensionObject
136    where
137        N: Into<NodeId>,
138        T: BinaryEncoder<T>,
139    {
140        // Serialize to extension object
141        let mut stream = Cursor::new(vec![0u8; encodable.byte_len()]);
142        let _ = encodable.encode(&mut stream);
143        ExtensionObject {
144            node_id: node_id.into(),
145            body: ExtensionObjectEncoding::ByteString(ByteString::from(stream.into_inner())),
146        }
147    }
148
149    /// Decodes the inner content of the extension object and returns it. The node id is ignored
150    /// for decoding. The caller supplies the binary encoder impl that should be used to extract
151    /// the data. Errors result in a decoding error.
152    pub fn decode_inner<T>(&self, decoding_options: &DecodingOptions) -> EncodingResult<T>
153    where
154        T: BinaryEncoder<T>,
155    {
156        match self.body {
157            ExtensionObjectEncoding::ByteString(ref byte_string) => {
158                if let Some(ref value) = byte_string.value {
159                    // let value = value.clone();
160                    let mut stream = Cursor::new(value);
161                    T::decode(&mut stream, decoding_options)
162                } else {
163                    Err(StatusCode::BadDecodingError)
164                }
165            }
166            _ => {
167                error!("decode_inner called on an unsupported ExtensionObject type");
168                Err(StatusCode::BadDecodingError)
169            }
170        }
171    }
172}