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