1use crate::string::*;
67use core::fmt::{Debug, Display};
68use core::hash::Hash;
69use core::ops::Deref;
70use iceoryx2_log::fail;
71
72#[derive(Debug, Clone, Copy, Eq, PartialEq)]
74pub enum SemanticStringError {
75 InvalidContent,
77 ExceedsMaximumLength,
79}
80
81impl From<StringModificationError> for SemanticStringError {
82 fn from(value: StringModificationError) -> Self {
83 match value {
84 StringModificationError::InsertWouldExceedCapacity => {
85 SemanticStringError::ExceedsMaximumLength
86 }
87 StringModificationError::InvalidCharacter => SemanticStringError::InvalidContent,
88 }
89 }
90}
91
92impl core::fmt::Display for SemanticStringError {
93 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
94 core::write!(f, "SemanticStringError::{self:?}")
95 }
96}
97
98impl core::error::Error for SemanticStringError {}
99
100#[doc(hidden)]
101pub mod internal {
102 use super::*;
103
104 pub trait SemanticStringAccessor<const CAPACITY: usize> {
105 unsafe fn new_empty() -> Self;
106 unsafe fn get_mut_string(&mut self) -> &mut StaticString<CAPACITY>;
107 fn is_invalid_content(string: &[u8]) -> bool;
108 fn does_contain_invalid_characters(string: &[u8]) -> bool;
109 }
110}
111
112pub trait SemanticString<const CAPACITY: usize>:
116 internal::SemanticStringAccessor<CAPACITY>
117 + Debug
118 + Display
119 + Sized
120 + Deref<Target = [u8]>
121 + PartialEq
122 + Eq
123 + Hash
124 + Clone
125 + Copy
126{
127 fn as_string(&self) -> &StaticString<CAPACITY>;
129
130 fn new(value: &[u8]) -> Result<Self, SemanticStringError> {
133 let msg = "Unable to create SemanticString";
134 let origin = "SemanticString::new()";
135
136 let mut new_self =
137 unsafe { <Self as internal::SemanticStringAccessor<CAPACITY>>::new_empty() };
138 fail!(from origin, when new_self.push_bytes(value),
139 "{} due to an invalid value \"{}\".", msg, as_escaped_string(value));
140
141 Ok(new_self)
142 }
143
144 unsafe fn new_unchecked(bytes: &[u8]) -> Self;
153
154 unsafe fn from_c_str(ptr: *const core::ffi::c_char) -> Result<Self, SemanticStringError> {
164 unsafe {
165 Self::new(core::slice::from_raw_parts(
166 ptr.cast(),
167 strnlen(ptr, CAPACITY + 1),
168 ))
169 }
170 }
171
172 fn as_bytes(&self) -> &[u8] {
174 self.as_string().as_bytes()
175 }
176
177 fn as_c_str(&self) -> *const core::ffi::c_char {
179 self.as_string().as_c_str()
180 }
181
182 fn capacity(&self) -> usize {
184 CAPACITY
185 }
186
187 fn find(&self, bytes: &[u8]) -> Option<usize> {
190 self.as_string().find(bytes)
191 }
192
193 fn rfind(&self, bytes: &[u8]) -> Option<usize> {
196 self.as_string().find(bytes)
197 }
198
199 fn is_full(&self) -> bool {
201 self.as_string().is_full()
202 }
203
204 fn is_empty(&self) -> bool {
206 self.as_string().is_empty()
207 }
208
209 fn len(&self) -> usize {
211 self.as_string().len()
212 }
213
214 fn insert(&mut self, idx: usize, byte: u8) -> Result<(), SemanticStringError> {
217 self.insert_bytes(idx, &[byte; 1])
218 }
219
220 fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), SemanticStringError> {
223 let msg = "Unable to insert byte string";
224 fail!(from self, when unsafe { self.get_mut_string().insert_bytes(idx, bytes) },
225 with SemanticStringError::ExceedsMaximumLength,
226 "{} \"{}\" since it would exceed the maximum allowed length of {}.",
227 msg, as_escaped_string(bytes), CAPACITY);
228
229 if Self::is_invalid_content(self.as_bytes()) {
230 unsafe { self.get_mut_string().remove_range(idx, bytes.len()) };
231 fail!(from self, with SemanticStringError::InvalidContent,
232 "{} \"{}\" since it would result in an illegal content.",
233 msg, as_escaped_string(bytes));
234 }
235
236 Ok(())
237 }
238
239 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]);
250
251 fn normalize(&self) -> Self;
256
257 fn pop(&mut self) -> Result<Option<u8>, SemanticStringError> {
260 if self.len() == 0 {
261 return Ok(None);
262 }
263
264 self.remove(self.len() - 1)
265 }
266
267 fn push(&mut self, byte: u8) -> Result<(), SemanticStringError> {
270 self.insert(self.len(), byte)
271 }
272
273 fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), SemanticStringError> {
276 self.insert_bytes(self.len(), bytes)
277 }
278
279 fn remove(&mut self, idx: usize) -> Result<Option<u8>, SemanticStringError> {
282 let mut temp = *self.as_string();
283 let value = temp.remove(idx);
284
285 if Self::is_invalid_content(temp.as_bytes()) {
286 fail!(from self, with SemanticStringError::InvalidContent,
287 "Unable to remove character at position {} since it would result in an illegal content.",
288 idx);
289 }
290
291 unsafe { *self.get_mut_string() = temp };
292 Ok(value)
293 }
294
295 fn remove_range(&mut self, idx: usize, len: usize) -> Result<(), SemanticStringError> {
298 let mut temp = *self.as_string();
299 temp.remove_range(idx, len);
300 if Self::is_invalid_content(temp.as_bytes()) {
301 fail!(from self, with SemanticStringError::InvalidContent,
302 "Unable to remove range from {} with length {} since it would result in the illegal content \"{}\".",
303 idx, len, temp);
304 }
305
306 unsafe { self.get_mut_string().remove_range(idx, len) };
307 Ok(())
308 }
309
310 fn retain<F: FnMut(u8) -> bool>(&mut self, f: F) -> Result<(), SemanticStringError> {
313 let mut temp = *self.as_string();
314 temp.retain(f);
315
316 if Self::is_invalid_content(temp.as_bytes()) {
317 fail!(from self, with SemanticStringError::InvalidContent,
318 "Unable to retain characters from string since it would result in the illegal content \"{}\".",
319 temp);
320 }
321
322 unsafe { *self.get_mut_string() = temp };
323
324 Ok(())
325 }
326
327 fn strip_prefix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
331 let mut temp = *self.as_string();
332 if !temp.strip_prefix(bytes) {
333 return Ok(false);
334 }
335
336 if Self::is_invalid_content(temp.as_bytes()) {
337 let mut prefix = StaticString::<123>::new();
338 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
339 fail!(from self, with SemanticStringError::InvalidContent,
340 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
341 prefix, temp);
342 }
343
344 unsafe { self.get_mut_string().strip_prefix(bytes) };
345
346 Ok(true)
347 }
348
349 fn strip_suffix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
353 let mut temp = *self.as_string();
354 if !temp.strip_suffix(bytes) {
355 return Ok(false);
356 }
357
358 if Self::is_invalid_content(temp.as_bytes()) {
359 let mut prefix = StaticString::<123>::new();
360 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
361 fail!(from self, with SemanticStringError::InvalidContent,
362 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
363 prefix, temp);
364 }
365
366 unsafe { self.get_mut_string().strip_suffix(bytes) };
367
368 Ok(true)
369 }
370
371 fn truncate(&mut self, new_len: usize) -> Result<(), SemanticStringError> {
373 let mut temp = *self.as_string();
374 temp.truncate(new_len);
375
376 if Self::is_invalid_content(temp.as_bytes()) {
377 fail!(from self, with SemanticStringError::InvalidContent,
378 "Unable to truncate characters to {} since it would result in the illegal content \"{}\".",
379 new_len, temp);
380 }
381
382 unsafe { self.get_mut_string().truncate(new_len) };
383 Ok(())
384 }
385}
386
387#[macro_export(local_inner_macros)]
390macro_rules! semantic_string {
391 {
392 $(#[$documentation:meta])*
393 name: $string_name:ident,
395 capacity: $capacity:expr,
397 invalid_content: $invalid_content:expr,
400 invalid_characters: $invalid_characters:expr,
403 normalize: $normalize:expr
406 } => {
407 $(#[$documentation])*
408 #[repr(C)]
409 #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, ZeroCopySend)]
410 pub struct $string_name {
411 value: iceoryx2_bb_container::string::StaticString<$capacity>
412 }
413
414 pub(crate) mod semantic_string_visitor_type {
416 pub(crate) struct $string_name;
417 }
418
419 impl<'de> serde::de::Visitor<'de> for semantic_string_visitor_type::$string_name {
420 type Value = $string_name;
421
422 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
423 formatter.write_str("a string containing the service name")
424 }
425
426 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
427 where
428 E: serde::de::Error,
429 {
430 match $string_name::new(v.as_bytes()) {
431 Ok(v) => Ok(v),
432 Err(v) => Err(E::custom(alloc::format!("invalid {} provided {:?}.", core::stringify!($string_name), v))),
433 }
434 }
435 }
436
437 impl<'de> serde::Deserialize<'de> for $string_name {
438 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
439 where
440 D: serde::Deserializer<'de>,
441 {
442 deserializer.deserialize_str(semantic_string_visitor_type::$string_name)
443 }
444 }
445
446 impl serde::Serialize for $string_name {
447 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
448 where
449 S: serde::Serializer,
450 {
451 serializer.serialize_str(core::str::from_utf8(self.as_bytes()).unwrap())
452 }
453 }
454 impl iceoryx2_bb_container::semantic_string::SemanticString<$capacity> for $string_name {
457 fn as_string(&self) -> &iceoryx2_bb_container::string::StaticString<$capacity> {
458 &self.value
459 }
460
461 fn normalize(&self) -> Self {
462 $normalize(self)
463 }
464
465 unsafe fn new_unchecked(bytes: &[u8]) -> Self {
466 Self {
467 value: unsafe { iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(bytes) },
468 }
469 }
470
471 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]) {
472 use iceoryx2_bb_container::string::String;
473 unsafe {
474 self.value.insert_bytes_unchecked(idx, bytes);
475 }
476 }
477 }
478
479 impl $string_name {
480 pub const unsafe fn new_unchecked_const(value: &[u8]) -> $string_name {
488 core::debug_assert!(value.len() <= $capacity);
489 $string_name {
490 value: unsafe { iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(value) },
491 }
492 }
493
494 pub const fn max_len() -> usize {
496 $capacity
497 }
498
499 pub const fn as_bytes_const(&self) -> &[u8] {
501 self.value.as_bytes_const()
502 }
503 }
504
505 impl core::fmt::Display for $string_name {
506 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
507 core::write!(f, "{}", self.value)
508 }
509 }
510
511 impl core::hash::Hash for $string_name {
512 fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
513 self.normalize().as_bytes().hash(state)
514 }
515 }
516
517 impl From<$string_name> for alloc::string::String {
518 fn from(value: $string_name) -> alloc::string::String {
519 unsafe { alloc::string::String::from_utf8_unchecked(value.as_bytes().to_vec()) }
521 }
522 }
523
524 impl From<&$string_name> for alloc::string::String {
525 fn from(value: &$string_name) -> alloc::string::String {
526 unsafe { alloc::string::String::from_utf8_unchecked(value.as_bytes().to_vec()) }
528 }
529 }
530
531 impl core::convert::TryFrom<&str> for $string_name {
532 type Error = iceoryx2_bb_container::semantic_string::SemanticStringError;
533
534 fn try_from(value: &str) -> Result<Self, Self::Error> {
535 Self::new(value.as_bytes())
536 }
537 }
538
539 impl PartialEq<$string_name> for $string_name {
540 fn eq(&self, other: &$string_name) -> bool {
541 *self.normalize().as_bytes() == *other.normalize().as_bytes()
542 }
543 }
544
545 impl PartialEq<&[u8]> for $string_name {
546 fn eq(&self, other: &&[u8]) -> bool {
547 let other = match $string_name::new(other) {
548 Ok(other) => other,
549 Err(_) => return false,
550 };
551
552 *self == other
553 }
554 }
555
556 impl PartialEq<&[u8]> for &$string_name {
557 fn eq(&self, other: &&[u8]) -> bool {
558 let other = match $string_name::new(other) {
559 Ok(other) => other,
560 Err(_) => return false,
561 };
562
563 **self == other
564 }
565 }
566
567 impl<const CAPACITY: usize> PartialEq<[u8; CAPACITY]> for $string_name {
568 fn eq(&self, other: &[u8; CAPACITY]) -> bool {
569 let other = match $string_name::new(other) {
570 Ok(other) => other,
571 Err(_) => return false,
572 };
573
574 *self == other
575 }
576 }
577
578 impl<const CAPACITY: usize> PartialEq<&[u8; CAPACITY]> for $string_name {
579 fn eq(&self, other: &&[u8; CAPACITY]) -> bool {
580 let other = match $string_name::new(*other) {
581 Ok(other) => other,
582 Err(_) => return false,
583 };
584
585 *self == other
586 }
587 }
588
589 impl PartialEq<&str> for &$string_name {
590 fn eq(&self, other: &&str) -> bool {
591 let other = match $string_name::new(other.as_bytes()) {
592 Ok(other) => other,
593 Err(_) => return false,
594 };
595
596 **self == other
597 }
598 }
599
600 impl core::ops::Deref for $string_name {
601 type Target = [u8];
602
603 fn deref(&self) -> &Self::Target {
604 use iceoryx2_bb_container::string::String;
605 self.value.as_bytes()
606 }
607 }
608
609 impl iceoryx2_bb_container::semantic_string::internal::SemanticStringAccessor<$capacity> for $string_name {
610 unsafe fn new_empty() -> Self {
611 Self {
612 value: iceoryx2_bb_container::string::StaticString::new(),
613 }
614 }
615
616 unsafe fn get_mut_string(&mut self) -> &mut iceoryx2_bb_container::string::StaticString<$capacity> {
617 &mut self.value
618 }
619
620 fn is_invalid_content(string: &[u8]) -> bool {
621 if Self::does_contain_invalid_characters(string) {
622 return true;
623 }
624
625 $invalid_content(string)
626 }
627
628 fn does_contain_invalid_characters(string: &[u8]) -> bool {
629 if core::str::from_utf8(string).is_err() {
630 return true;
631 }
632
633 $invalid_characters(string)
634 }
635 }
636 }
637}