1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
use std::{ffi::CString, fmt, ptr, slice, str};
use open62541_sys::UA_String_fromChars;
use crate::{ArrayValue, Error};
crate::data_type!(String);
// In the implementation below, remember that `self.0.data` may be `UA_EMPTY_ARRAY_SENTINEL` for any
// strings of `length` 0. It may also be `ptr::null()` for "invalid" strings. This is similar to how
// OPC UA treats arrays (which also distinguishes between empty and invalid instances).
impl String {
/// Creates string from string slice.
///
/// # Errors
///
/// The string must not contain any NUL bytes.
pub fn new(s: &str) -> Result<Self, Error> {
// We do not know for sure if `open62541` handles strings with contained NUL bytes correctly
// in all situations. We avoid this entirely (at least for now). We may revisit this later.
let src =
CString::new(s).map_err(|_| Error::internal("string should not contain NUL bytes"))?;
let str = unsafe { UA_String_fromChars(src.as_ptr()) };
Ok(Self(str))
}
/// Creates invalid string (as defined by OPC UA).
// TODO: The OPC UA specification calls invalid strings "null". Consider changing this to match.
#[allow(dead_code)] // This is unused for now.
pub(crate) fn invalid() -> Self {
let str = unsafe { UA_String_fromChars(ptr::null()) };
Self(str)
}
/// Creates empty string.
#[allow(dead_code)] // This is unused for now.
pub(crate) fn empty() -> Self {
Self::new("").unwrap()
}
/// Checks if string is invalid.
///
/// The invalid state is defined by OPC UA. It is a third state which is distinct from empty and
/// regular (non-empty) strings.
#[must_use]
pub fn is_invalid(&self) -> bool {
matches!(self.array_value(), ArrayValue::Invalid)
}
/// Checks if string is empty.
#[must_use]
pub fn is_empty(&self) -> bool {
matches!(self.array_value(), ArrayValue::Empty)
}
#[deprecated(note = "use `Self::as_bytes()` instead")]
#[must_use]
pub fn as_slice(&self) -> Option<&[u8]> {
self.as_bytes()
}
/// Returns string contents as byte slice.
///
/// This may return [`None`] when the string itself is invalid (as defined by OPC UA).
#[must_use]
pub fn as_bytes(&self) -> Option<&[u8]> {
// Internally, `open62541` represents strings as `Byte` array and has the same special cases
// as regular arrays, i.e. empty and invalid states.
match self.array_value() {
ArrayValue::Invalid => None,
ArrayValue::Empty => Some(&[]),
ArrayValue::Valid(data) => {
// `self.0.data` is valid, so we may use `self.0.length` now.
Some(unsafe { slice::from_raw_parts(data.as_ptr(), self.0.length) })
}
}
}
/// Returns string contents as string slice.
///
/// This may return [`None`] when the string itself is invalid (as defined by OPC UA) or when it
/// is not valid Unicode (UTF-8).
#[must_use]
pub fn as_str(&self) -> Option<&str> {
self.as_bytes().and_then(|slice| str::from_utf8(slice).ok())
}
fn array_value(&self) -> ArrayValue<u8> {
// Internally, `open62541` represents strings as `Byte` array and has the same special cases
// as regular arrays, i.e. empty and invalid states.
ArrayValue::from_ptr(self.0.data)
}
}
impl fmt::Display for String {
/// Creates string from [`String`] value.
///
/// # Examples
///
/// ```
/// use open62541::{ua, DataType as _};
///
/// let node_id = ua::String::init();
/// let str = node_id.to_string();
///
/// assert_eq!(str, "");
/// ```
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// Display invalid strings as empty strings.
f.write_str(self.as_str().unwrap_or(""))
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for String {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
self.as_str()
.ok_or(serde::ser::Error::custom("String should be valid"))
.and_then(|str| serializer.serialize_str(str))
}
}
#[cfg(test)]
mod tests {
use crate::ua;
#[test]
fn valid_string() {
let str = ua::String::new("lorem ipsum").expect("should parse string");
assert_eq!(str.as_str().expect("should display string"), "lorem ipsum");
assert_eq!(str.to_string(), "lorem ipsum");
}
#[test]
fn empty_string() {
// Empty strings may have an internal representation in `UA_String` that contains invalid or
// null pointers. This must not cause any problems.
let str = ua::String::new("").expect("should parse empty string");
assert_eq!(str.as_str().expect("should display empty string"), "");
assert_eq!(str.to_string(), "");
}
}