Skip to main content

opcua/types/
string.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 `UAString`.
6
7use std::{
8    fmt,
9    io::{Read, Write},
10};
11
12use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
13
14use crate::types::{
15    encoding::{
16        process_decode_io_result, process_encode_io_result, write_i32, BinaryEncoder,
17        DecodingOptions, EncodingResult,
18    },
19    status_codes::StatusCode,
20};
21
22/// To avoid naming conflict hell, the OPC UA String type is typed `UAString` so it does not collide
23/// with the Rust `String`.
24///
25/// A string contains UTF-8 encoded characters or a null value. A null value is distinct from
26/// being an empty string so internally, the code maintains that distinction by holding the value
27/// as an `Option<String>`.
28#[derive(Eq, PartialEq, Debug, Clone, Hash)]
29pub struct UAString {
30    value: Option<String>,
31}
32
33impl fmt::Display for UAString {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        if let Some(ref value) = self.value {
36            write!(f, "{}", value)
37        } else {
38            write!(f, "[null]")
39        }
40    }
41}
42
43impl Serialize for UAString {
44    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
45    where
46        S: Serializer,
47    {
48        if let Some(s) = self.value.as_ref() {
49            serializer.serialize_str(&s)
50        } else {
51            serializer.serialize_none()
52        }
53    }
54}
55
56struct UAStringVisitor;
57
58impl<'de> serde::de::Visitor<'de> for UAStringVisitor {
59    type Value = UAString;
60    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
61        write!(formatter, "a string value or null")
62    }
63
64    fn visit_none<E>(self) -> Result<Self::Value, E>
65    where
66        E: de::Error,
67    {
68        Ok(Self::Value::null())
69    }
70
71    fn visit_some<D>(self, deserializer: D) -> Result<Self::Value, D::Error>
72    where
73        D: Deserializer<'de>,
74    {
75        deserializer.deserialize_str(self)
76    }
77
78    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
79    where
80        E: de::Error,
81    {
82        Ok(Self::Value::from(v))
83    }
84}
85
86impl<'de> Deserialize<'de> for UAString {
87    fn deserialize<D>(deserializer: D) -> Result<UAString, D::Error>
88    where
89        D: Deserializer<'de>,
90    {
91        deserializer.deserialize_option(UAStringVisitor)
92    }
93}
94
95impl BinaryEncoder<UAString> for UAString {
96    fn byte_len(&self) -> usize {
97        // Length plus the actual string length in bytes for a non-null string.
98        4 + if self.value.is_none() {
99            0
100        } else {
101            self.value.as_ref().unwrap().len()
102        }
103    }
104
105    fn encode<S: Write>(&self, stream: &mut S) -> EncodingResult<usize> {
106        // Strings are encoded as UTF8 chars preceded by an Int32 length. A -1 indicates a null string
107        if self.value.is_none() {
108            write_i32(stream, -1)
109        } else {
110            let value = self.value.as_ref().unwrap();
111            let mut size: usize = 0;
112            size += write_i32(stream, value.len() as i32)?;
113            let buf = value.as_bytes();
114            size += process_encode_io_result(stream.write(buf))?;
115            assert_eq!(size, self.byte_len());
116            Ok(size)
117        }
118    }
119
120    fn decode<S: Read>(stream: &mut S, decoding_options: &DecodingOptions) -> EncodingResult<Self> {
121        let len = i32::decode(stream, decoding_options)?;
122        // Null string?
123        if len == -1 {
124            Ok(UAString::null())
125        } else if len < -1 {
126            error!("String buf length is a negative number {}", len);
127            Err(StatusCode::BadDecodingError)
128        } else if len as usize > decoding_options.max_string_length {
129            error!(
130                "String buf length {} exceeds decoding limit {}",
131                len, decoding_options.max_string_length
132            );
133            Err(StatusCode::BadDecodingError)
134        } else {
135            // Create a buffer filled with zeroes and read the string over the top
136            let mut buf = vec![0u8; len as usize];
137            process_decode_io_result(stream.read_exact(&mut buf))?;
138            let value = String::from_utf8(buf).map_err(|err| {
139                trace!("Decoded string was not valid UTF-8 - {}", err.to_string());
140                StatusCode::BadDecodingError
141            })?;
142            Ok(UAString::from(value))
143        }
144    }
145}
146
147impl From<UAString> for String {
148    fn from(value: UAString) -> Self {
149        value.as_ref().to_string()
150    }
151}
152
153impl AsRef<str> for UAString {
154    fn as_ref(&self) -> &str {
155        if self.is_null() {
156            ""
157        } else {
158            self.value.as_ref().unwrap()
159        }
160    }
161}
162
163impl<'a> From<&'a str> for UAString {
164    fn from(value: &'a str) -> Self {
165        Self::from(value.to_string())
166    }
167}
168
169impl From<&String> for UAString {
170    fn from(value: &String) -> Self {
171        UAString {
172            value: Some(value.clone()),
173        }
174    }
175}
176
177impl From<String> for UAString {
178    fn from(value: String) -> Self {
179        UAString { value: Some(value) }
180    }
181}
182
183impl Default for UAString {
184    fn default() -> Self {
185        UAString::null()
186    }
187}
188
189impl<'a, 'b> PartialEq<str> for UAString {
190    fn eq(&self, other: &str) -> bool {
191        match self.value {
192            None => false,
193            Some(ref v) => v.eq(other),
194        }
195    }
196}
197
198impl UAString {
199    pub fn value(&self) -> &Option<String> {
200        &self.value
201    }
202
203    pub fn set_value(&mut self, value: Option<String>) {
204        self.value = value;
205    }
206
207    /// Returns true if the string is null or empty, false otherwise
208    pub fn is_empty(&self) -> bool {
209        if self.value.is_none() {
210            true
211        } else {
212            self.value.as_ref().unwrap().is_empty()
213        }
214    }
215
216    /// Returns the length of the string in bytes or -1 for null.
217    pub fn len(&self) -> isize {
218        if self.value.is_none() {
219            -1
220        } else {
221            self.value.as_ref().unwrap().len() as isize
222        }
223    }
224
225    /// Create a null string (not the same as an empty string).
226    pub fn null() -> UAString {
227        UAString { value: None }
228    }
229
230    /// Test if the string is null.
231    pub fn is_null(&self) -> bool {
232        self.value.is_none()
233    }
234
235    /// This function is meant for use with NumericRange. It creates a substring from this string
236    /// from min up to and inclusive of max. Note that min must have an index within the string
237    /// but max is allowed to be beyond the end in which case the remainder of the string is
238    /// returned (see docs for NumericRange).
239    pub fn substring(&self, min: usize, max: usize) -> Result<UAString, ()> {
240        if let Some(ref v) = self.value() {
241            if min >= v.len() {
242                Err(())
243            } else {
244                let max = if max >= v.len() { v.len() - 1 } else { max };
245                Ok(UAString::from(&v[min..=max]))
246            }
247        } else {
248            Err(())
249        }
250    }
251}
252
253#[test]
254fn string_null() {
255    let s = UAString::null();
256    assert!(s.is_null());
257    assert!(s.is_empty());
258    assert_eq!(s.len(), -1);
259}
260
261#[test]
262fn string_empty() {
263    let s = UAString::from("");
264    assert!(!s.is_null());
265    assert!(s.is_empty());
266    assert_eq!(s.len(), 0);
267}
268
269#[test]
270fn string_value() {
271    let v = "Mary had a little lamb";
272    let s = UAString::from(v);
273    assert!(!s.is_null());
274    assert!(!s.is_empty());
275    assert_eq!(s.as_ref(), v);
276}
277
278#[test]
279fn string_eq() {
280    let s = UAString::null();
281    assert!(!s.eq(""));
282
283    let s = UAString::from("");
284    assert!(s.eq(""));
285
286    let s = UAString::from("Sunshine");
287    assert!(s.ne("Moonshine"));
288    assert!(s.eq("Sunshine"));
289    assert!(!s.eq("Sunshine "));
290}
291
292#[test]
293fn string_substring() {
294    let a = "Mary had a little lamb";
295    let v = UAString::from(a);
296    let v2 = v.substring(0, 4).unwrap();
297    let a2 = v2.as_ref();
298    assert_eq!(a2, "Mary ");
299
300    let v2 = v.substring(2, 2).unwrap();
301    let a2 = v2.as_ref();
302    assert_eq!(a2, "r");
303
304    let v2 = v.substring(0, 2000).unwrap();
305    assert_eq!(v, v2);
306    assert_eq!(v2.as_ref(), a);
307
308    assert!(v.substring(22, 10000).is_err());
309
310    assert!(UAString::null().substring(0, 0).is_err());
311}
312
313/// An XML element.
314pub type XmlElement = UAString;