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