1use crate::byte_string::FixedSizeByteStringModificationError;
64use crate::byte_string::{as_escaped_string, strnlen, FixedSizeByteString};
65use iceoryx2_bb_log::fail;
66use std::fmt::{Debug, Display};
67use std::hash::Hash;
68use std::ops::Deref;
69
70#[derive(Debug, Clone, Copy, Eq, PartialEq)]
72pub enum SemanticStringError {
73 InvalidContent,
75 ExceedsMaximumLength,
77}
78
79impl From<FixedSizeByteStringModificationError> for SemanticStringError {
80 fn from(_value: FixedSizeByteStringModificationError) -> Self {
81 SemanticStringError::ExceedsMaximumLength
82 }
83}
84
85impl std::fmt::Display for SemanticStringError {
86 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87 std::write!(f, "SemanticStringError::{:?}", self)
88 }
89}
90
91impl std::error::Error for SemanticStringError {}
92
93#[doc(hidden)]
94pub mod internal {
95 use super::*;
96
97 pub trait SemanticStringAccessor<const CAPACITY: usize> {
98 unsafe fn new_empty() -> Self;
99 unsafe fn get_mut_string(&mut self) -> &mut FixedSizeByteString<CAPACITY>;
100 fn is_invalid_content(string: &[u8]) -> bool;
101 fn does_contain_invalid_characters(string: &[u8]) -> bool;
102 }
103}
104
105pub trait SemanticString<const CAPACITY: usize>:
109 internal::SemanticStringAccessor<CAPACITY>
110 + Debug
111 + Display
112 + Sized
113 + Deref<Target = [u8]>
114 + PartialEq
115 + Eq
116 + Hash
117{
118 fn as_string(&self) -> &FixedSizeByteString<CAPACITY>;
120
121 fn new(value: &[u8]) -> Result<Self, SemanticStringError> {
124 let msg = "Unable to create SemanticString";
125 let origin = "SemanticString::new()";
126
127 let mut new_self =
128 unsafe { <Self as internal::SemanticStringAccessor<CAPACITY>>::new_empty() };
129 fail!(from origin, when new_self.push_bytes(value),
130 "{} due to an invalid value \"{}\".", msg, as_escaped_string(value));
131
132 Ok(new_self)
133 }
134
135 unsafe fn new_unchecked(bytes: &[u8]) -> Self;
144
145 unsafe fn from_c_str(ptr: *const std::ffi::c_char) -> Result<Self, SemanticStringError> {
155 Self::new(std::slice::from_raw_parts(
156 ptr.cast(),
157 strnlen(ptr, CAPACITY + 1),
158 ))
159 }
160
161 fn as_bytes(&self) -> &[u8] {
163 self.as_string().as_bytes()
164 }
165
166 fn as_c_str(&self) -> *const std::ffi::c_char {
168 self.as_string().as_c_str()
169 }
170
171 fn capacity(&self) -> usize {
173 self.as_string().capacity()
174 }
175
176 fn find(&self, bytes: &[u8]) -> Option<usize> {
179 self.as_string().find(bytes)
180 }
181
182 fn rfind(&self, bytes: &[u8]) -> Option<usize> {
185 self.as_string().find(bytes)
186 }
187
188 fn is_full(&self) -> bool {
190 self.as_string().is_full()
191 }
192
193 fn is_empty(&self) -> bool {
195 self.as_string().is_empty()
196 }
197
198 fn len(&self) -> usize {
200 self.as_string().len()
201 }
202
203 fn insert(&mut self, idx: usize, byte: u8) -> Result<(), SemanticStringError> {
206 self.insert_bytes(idx, &[byte; 1])
207 }
208
209 fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), SemanticStringError> {
212 let msg = "Unable to insert byte string";
213 fail!(from self, when unsafe { self.get_mut_string().insert_bytes(idx, bytes) },
214 with SemanticStringError::ExceedsMaximumLength,
215 "{} \"{}\" since it would exceed the maximum allowed length of {}.",
216 msg, as_escaped_string(bytes), CAPACITY);
217
218 if Self::is_invalid_content(self.as_bytes()) {
219 unsafe { self.get_mut_string().remove_range(idx, bytes.len()) };
220 fail!(from self, with SemanticStringError::InvalidContent,
221 "{} \"{}\" since it would result in an illegal content.",
222 msg, as_escaped_string(bytes));
223 }
224
225 Ok(())
226 }
227
228 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]);
239
240 fn normalize(&self) -> Self;
245
246 fn pop(&mut self) -> Result<Option<u8>, SemanticStringError> {
249 if self.len() == 0 {
250 return Ok(None);
251 }
252
253 Ok(Some(self.remove(self.len() - 1)?))
254 }
255
256 fn push(&mut self, byte: u8) -> Result<(), SemanticStringError> {
259 self.insert(self.len(), byte)
260 }
261
262 fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), SemanticStringError> {
265 self.insert_bytes(self.len(), bytes)
266 }
267
268 fn remove(&mut self, idx: usize) -> Result<u8, SemanticStringError> {
271 let value = unsafe { self.get_mut_string().remove(idx) };
272
273 if Self::is_invalid_content(self.as_bytes()) {
274 unsafe { self.get_mut_string().insert(idx, value).unwrap() };
275 fail!(from self, with SemanticStringError::InvalidContent,
276 "Unable to remove character at position {} since it would result in an illegal content.",
277 idx);
278 }
279
280 Ok(value)
281 }
282
283 fn remove_range(&mut self, idx: usize, len: usize) -> Result<(), SemanticStringError> {
286 let mut temp = *self.as_string();
287 temp.remove_range(idx, len);
288 if Self::is_invalid_content(temp.as_bytes()) {
289 fail!(from self, with SemanticStringError::InvalidContent,
290 "Unable to remove range from {} with lenght {} since it would result in the illegal content \"{}\".",
291 idx, len, temp);
292 }
293
294 unsafe { self.get_mut_string().remove_range(idx, len) };
295 Ok(())
296 }
297
298 fn retain<F: FnMut(u8) -> bool>(&mut self, f: F) -> Result<(), SemanticStringError> {
301 let mut temp = *self.as_string();
302 let f = temp.retain_impl(f);
303
304 if Self::is_invalid_content(temp.as_bytes()) {
305 fail!(from self, with SemanticStringError::InvalidContent,
306 "Unable to retain characters from string since it would result in the illegal content \"{}\".",
307 temp);
308 }
309
310 unsafe { self.get_mut_string().retain(f) };
311 Ok(())
312 }
313
314 fn strip_prefix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
318 let mut temp = *self.as_string();
319 if !temp.strip_prefix(bytes) {
320 return Ok(false);
321 }
322
323 if Self::is_invalid_content(temp.as_bytes()) {
324 let mut prefix = FixedSizeByteString::<123>::new();
325 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
326 fail!(from self, with SemanticStringError::InvalidContent,
327 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
328 prefix, temp);
329 }
330
331 unsafe { self.get_mut_string().strip_prefix(bytes) };
332
333 Ok(true)
334 }
335
336 fn strip_suffix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
340 let mut temp = *self.as_string();
341 if !temp.strip_suffix(bytes) {
342 return Ok(false);
343 }
344
345 if Self::is_invalid_content(temp.as_bytes()) {
346 let mut prefix = FixedSizeByteString::<123>::new();
347 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
348 fail!(from self, with SemanticStringError::InvalidContent,
349 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
350 prefix, temp);
351 }
352
353 unsafe { self.get_mut_string().strip_suffix(bytes) };
354
355 Ok(true)
356 }
357
358 fn truncate(&mut self, new_len: usize) -> Result<(), SemanticStringError> {
360 let mut temp = *self.as_string();
361 temp.truncate(new_len);
362
363 if Self::is_invalid_content(temp.as_bytes()) {
364 fail!(from self, with SemanticStringError::InvalidContent,
365 "Unable to truncate characters to {} since it would result in the illegal content \"{}\".",
366 new_len, temp);
367 }
368
369 unsafe { self.get_mut_string().truncate(new_len) };
370 Ok(())
371 }
372}
373
374#[macro_export(local_inner_macros)]
377macro_rules! semantic_string {
378 {$(#[$documentation:meta])*
379 name: $string_name:ident,
381 capacity: $capacity:expr,
383 invalid_content: $invalid_content:expr,
386 invalid_characters: $invalid_characters:expr,
389 normalize: $normalize:expr} => {
392 $(#[$documentation])*
393 #[derive(Debug, Clone, Copy, Eq)]
394 pub struct $string_name {
395 value: iceoryx2_bb_container::byte_string::FixedSizeByteString<$capacity>
396 }
397
398 pub(crate) mod VisitorType {
400 pub(crate) struct $string_name;
401 }
402
403 impl<'de> serde::de::Visitor<'de> for VisitorType::$string_name {
404 type Value = $string_name;
405
406 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
407 formatter.write_str("a string containing the service name")
408 }
409
410 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
411 where
412 E: serde::de::Error,
413 {
414 match $string_name::new(v.as_bytes()) {
415 Ok(v) => Ok(v),
416 Err(v) => Err(E::custom(std::format!("invalid {} provided {:?}.", std::stringify!($string_name), v))),
417 }
418 }
419 }
420
421 impl<'de> serde::Deserialize<'de> for $string_name {
422 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
423 where
424 D: serde::Deserializer<'de>,
425 {
426 deserializer.deserialize_str(VisitorType::$string_name)
427 }
428 }
429
430 impl serde::Serialize for $string_name {
431 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
432 where
433 S: serde::Serializer,
434 {
435 serializer.serialize_str(std::str::from_utf8(self.as_bytes()).unwrap())
436 }
437 }
438 impl iceoryx2_bb_container::semantic_string::SemanticString<$capacity> for $string_name {
441 fn as_string(&self) -> &iceoryx2_bb_container::byte_string::FixedSizeByteString<$capacity> {
442 &self.value
443 }
444
445 fn normalize(&self) -> Self {
446 $normalize(self)
447 }
448
449 unsafe fn new_unchecked(bytes: &[u8]) -> Self {
450 Self {
451 value: iceoryx2_bb_container::byte_string::FixedSizeByteString::new_unchecked(bytes),
452 }
453 }
454
455 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]) {
456 self.value.insert_bytes_unchecked(idx, bytes);
457 }
458 }
459
460 impl $string_name {
461 pub const fn max_len() -> usize {
463 $capacity
464 }
465 }
466
467 impl std::fmt::Display for $string_name {
468 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
469 std::write!(f, "{}", self.value)
470 }
471 }
472
473 impl Hash for $string_name {
474 fn hash<H: Hasher>(&self, state: &mut H) {
475 self.normalize().as_bytes().hash(state)
476 }
477 }
478
479 impl From<$string_name> for String {
480 fn from(value: $string_name) -> String {
481 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
483 }
484 }
485
486 impl From<&$string_name> for String {
487 fn from(value: &$string_name) -> String {
488 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
490 }
491 }
492
493 impl std::convert::TryFrom<&str> for $string_name {
494 type Error = iceoryx2_bb_container::semantic_string::SemanticStringError;
495
496 fn try_from(value: &str) -> Result<Self, Self::Error> {
497 Self::new(value.as_bytes())
498 }
499 }
500
501 impl PartialEq<$string_name> for $string_name {
502 fn eq(&self, other: &$string_name) -> bool {
503 *self.normalize().as_bytes() == *other.normalize().as_bytes()
504 }
505 }
506
507 impl PartialEq<&[u8]> for $string_name {
508 fn eq(&self, other: &&[u8]) -> bool {
509 let other = match $string_name::new(other) {
510 Ok(other) => other,
511 Err(_) => return false,
512 };
513
514 *self == other
515 }
516 }
517
518 impl PartialEq<&[u8]> for &$string_name {
519 fn eq(&self, other: &&[u8]) -> bool {
520 let other = match $string_name::new(other) {
521 Ok(other) => other,
522 Err(_) => return false,
523 };
524
525 **self == other
526 }
527 }
528
529 impl<const CAPACITY: usize> PartialEq<[u8; CAPACITY]> for $string_name {
530 fn eq(&self, other: &[u8; CAPACITY]) -> bool {
531 let other = match $string_name::new(other) {
532 Ok(other) => other,
533 Err(_) => return false,
534 };
535
536 *self == other
537 }
538 }
539
540 impl<const CAPACITY: usize> PartialEq<&[u8; CAPACITY]> for $string_name {
541 fn eq(&self, other: &&[u8; CAPACITY]) -> bool {
542 let other = match $string_name::new(*other) {
543 Ok(other) => other,
544 Err(_) => return false,
545 };
546
547 *self == other
548 }
549 }
550
551 impl std::ops::Deref for $string_name {
552 type Target = [u8];
553
554 fn deref(&self) -> &Self::Target {
555 self.value.as_bytes()
556 }
557 }
558
559 impl iceoryx2_bb_container::semantic_string::internal::SemanticStringAccessor<$capacity> for $string_name {
560 unsafe fn new_empty() -> Self {
561 Self {
562 value: iceoryx2_bb_container::byte_string::FixedSizeByteString::new(),
563 }
564 }
565
566 unsafe fn get_mut_string(&mut self) -> &mut iceoryx2_bb_container::byte_string::FixedSizeByteString<$capacity> {
567 &mut self.value
568 }
569
570 fn is_invalid_content(string: &[u8]) -> bool {
571 if Self::does_contain_invalid_characters(string) {
572 return true;
573 }
574
575 $invalid_content(string)
576 }
577
578 fn does_contain_invalid_characters(string: &[u8]) -> bool {
579 if core::str::from_utf8(string).is_err() {
580 return true;
581 }
582
583 $invalid_characters(string)
584 }
585 }
586
587 };
588}