1use std::{
8 fmt,
9 io::{Read, Write},
10};
11
12use crate::{
13 encoding::{process_decode_io_result, process_encode_io_result, write_i32, EncodingResult},
14 read_i32, DecodingOptions, Error, OutOfRange, SimpleBinaryDecodable, SimpleBinaryEncodable,
15 UaNullable,
16};
17
18#[derive(Eq, PartialEq, Debug, Clone, Hash)]
25pub struct UAString {
26 value: Option<String>,
27}
28
29impl fmt::Display for UAString {
30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
31 if let Some(ref value) = self.value {
32 write!(f, "{value}")
33 } else {
34 write!(f, "[null]")
35 }
36 }
37}
38
39impl UaNullable for UAString {
40 fn is_ua_null(&self) -> bool {
41 self.is_null()
42 }
43}
44
45#[cfg(feature = "json")]
46mod json {
47 use std::io::{Read, Write};
48 use struson::{
49 reader::{JsonReader, JsonStreamReader, ValueType},
50 writer::{JsonStreamWriter, JsonWriter},
51 };
52
53 use crate::json::{Context, JsonDecodable, JsonEncodable};
54
55 use super::{EncodingResult, UAString};
56
57 impl JsonEncodable for UAString {
58 fn encode(
59 &self,
60 stream: &mut JsonStreamWriter<&mut dyn Write>,
61 _ctx: &Context<'_>,
62 ) -> EncodingResult<()> {
63 if let Some(s) = self.value() {
64 stream.string_value(s)?;
65 } else {
66 stream.null_value()?;
67 }
68
69 Ok(())
70 }
71 }
72
73 impl JsonDecodable for UAString {
74 fn decode(
75 stream: &mut JsonStreamReader<&mut dyn Read>,
76 _ctx: &Context<'_>,
77 ) -> EncodingResult<Self> {
78 match stream.peek()? {
79 ValueType::String => Ok(stream.next_string()?.into()),
80 _ => {
81 stream.next_null()?;
82 Ok(UAString::null())
83 }
84 }
85 }
86 }
87}
88
89impl SimpleBinaryEncodable for UAString {
90 fn byte_len(&self) -> usize {
91 4 + match &self.value {
93 Some(s) => s.len(),
94 None => 0,
95 }
96 }
97
98 fn encode<S: Write + ?Sized>(&self, stream: &mut S) -> EncodingResult<()> {
99 match &self.value {
101 Some(s) => {
102 write_i32(stream, s.len() as i32)?;
103 let buf = s.as_bytes();
104 process_encode_io_result(stream.write_all(buf))
105 }
106 None => write_i32(stream, -1),
107 }
108 }
109}
110
111impl SimpleBinaryDecodable for UAString {
112 fn decode<S: Read + ?Sized>(
113 stream: &mut S,
114 decoding_options: &DecodingOptions,
115 ) -> EncodingResult<Self> {
116 let len = read_i32(stream)?;
117 if len == -1 {
119 Ok(UAString::null())
120 } else if len < -1 {
121 Err(Error::decoding(format!(
122 "String buf length is a negative number {len}"
123 )))
124 } else if len as usize > decoding_options.max_string_length {
125 Err(Error::decoding(format!(
126 "String buf length {} exceeds decoding limit {}",
127 len, decoding_options.max_string_length
128 )))
129 } else {
130 let mut buf = vec![0u8; len as usize];
132 process_decode_io_result(stream.read_exact(&mut buf))?;
133 let value = String::from_utf8(buf).map_err(|err| {
134 Error::decoding(format!("Decoded string was not valid UTF-8 - {err}"))
135 })?;
136 Ok(UAString::from(value))
137 }
138 }
139}
140
141#[cfg(feature = "xml")]
142mod xml {
143 use crate::xml::*;
144 use std::io::{Read, Write};
145
146 use super::UAString;
147
148 impl XmlType for UAString {
149 const TAG: &'static str = "String";
150 }
151
152 impl XmlEncodable for UAString {
153 fn encode(
154 &self,
155 writer: &mut XmlStreamWriter<&mut dyn Write>,
156 _ctx: &Context<'_>,
157 ) -> Result<(), Error> {
158 if let Some(s) = self.value() {
159 writer.write_text(s)?;
160 }
161
162 Ok(())
163 }
164 }
165
166 impl XmlDecodable for UAString {
167 fn decode(
168 read: &mut XmlStreamReader<&mut dyn Read>,
169 _context: &Context<'_>,
170 ) -> Result<Self, Error> {
171 Ok(read.consume_as_text()?.into())
172 }
173 }
174}
175
176impl From<UAString> for String {
177 fn from(value: UAString) -> Self {
178 value.as_ref().to_string()
179 }
180}
181
182impl AsRef<str> for UAString {
183 fn as_ref(&self) -> &str {
184 if self.is_null() {
185 ""
186 } else {
187 self.value.as_ref().unwrap()
188 }
189 }
190}
191
192impl<'a> From<&'a str> for UAString {
193 fn from(value: &'a str) -> Self {
194 Self::from(value.to_string())
195 }
196}
197
198impl From<&String> for UAString {
199 fn from(value: &String) -> Self {
200 UAString {
201 value: Some(value.clone()),
202 }
203 }
204}
205
206impl From<String> for UAString {
207 fn from(value: String) -> Self {
208 UAString { value: Some(value) }
209 }
210}
211
212impl From<Option<String>> for UAString {
213 fn from(value: Option<String>) -> Self {
214 UAString { value }
215 }
216}
217
218impl Default for UAString {
219 fn default() -> Self {
220 UAString::null()
221 }
222}
223
224impl PartialEq<str> for UAString {
225 fn eq(&self, other: &str) -> bool {
226 match self.value {
227 None => false,
228 Some(ref v) => v.eq(other),
229 }
230 }
231}
232
233impl PartialEq<&str> for UAString {
234 fn eq(&self, other: &&str) -> bool {
235 match self.value {
236 None => false,
237 Some(ref v) => v.eq(other),
238 }
239 }
240}
241
242impl PartialEq<&String> for UAString {
243 fn eq(&self, other: &&String) -> bool {
244 match self.value {
245 None => false,
246 Some(ref v) => v.eq(*other),
247 }
248 }
249}
250
251impl PartialEq<String> for UAString {
252 fn eq(&self, other: &String) -> bool {
253 match self.value {
254 None => false,
255 Some(ref v) => v.eq(other),
256 }
257 }
258}
259
260impl UAString {
261 pub fn value(&self) -> &Option<String> {
263 &self.value
264 }
265
266 pub fn set_value(&mut self, value: Option<String>) {
268 self.value = value;
269 }
270
271 pub fn is_empty(&self) -> bool {
273 self.value.is_none() || self.value.as_ref().is_some_and(|v| v.is_empty())
274 }
275
276 pub fn len(&self) -> isize {
278 if self.value.is_none() {
279 -1
280 } else {
281 self.value.as_ref().unwrap().len() as isize
282 }
283 }
284
285 pub fn null() -> UAString {
287 UAString { value: None }
288 }
289
290 pub fn is_null(&self) -> bool {
292 self.value.is_none()
293 }
294
295 pub fn substring(&self, min: usize, max: usize) -> Result<UAString, OutOfRange> {
300 if let Some(ref v) = self.value() {
301 if min >= v.len() {
302 Err(OutOfRange)
303 } else {
304 let max = if max >= v.len() { v.len() - 1 } else { max };
305 Ok(UAString::from(&v[min..=max]))
306 }
307 } else {
308 Err(OutOfRange)
309 }
310 }
311}
312
313#[test]
314fn string_null() {
315 let s = UAString::null();
316 assert!(s.is_null());
317 assert!(s.is_empty());
318 assert_eq!(s.len(), -1);
319}
320
321#[test]
322fn string_empty() {
323 let s = UAString::from("");
324 assert!(!s.is_null());
325 assert!(s.is_empty());
326 assert_eq!(s.len(), 0);
327}
328
329#[test]
330fn string_value() {
331 let v = "Mary had a little lamb";
332 let s = UAString::from(v);
333 assert!(!s.is_null());
334 assert!(!s.is_empty());
335 assert_eq!(s.as_ref(), v);
336}
337
338#[test]
339#[allow(clippy::comparison_to_empty)]
340fn string_eq() {
341 let s = UAString::null();
342 assert!(!s.eq(""));
343
344 let s = UAString::from("");
345 assert!(s.eq(""));
346
347 let s = UAString::from("Sunshine");
348 assert!(s.ne("Moonshine"));
349 assert!(s.eq("Sunshine"));
350 assert!(!s.eq("Sunshine "));
351}
352
353#[test]
354fn string_substring() {
355 let a = "Mary had a little lamb";
356 let v = UAString::from(a);
357 let v2 = v.substring(0, 4).unwrap();
358 let a2 = v2.as_ref();
359 assert_eq!(a2, "Mary ");
360
361 let v2 = v.substring(2, 2).unwrap();
362 let a2 = v2.as_ref();
363 assert_eq!(a2, "r");
364
365 let v2 = v.substring(0, 2000).unwrap();
366 assert_eq!(v, v2);
367 assert_eq!(v2.as_ref(), a);
368
369 assert!(v.substring(22, 10000).is_err());
370
371 assert!(UAString::null().substring(0, 0).is_err());
372}