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 Self::new(core::slice::from_raw_parts(
166 ptr.cast(),
167 strnlen(ptr, CAPACITY + 1),
168 ))
169 }
170
171 fn as_bytes(&self) -> &[u8] {
173 self.as_string().as_bytes()
174 }
175
176 fn as_c_str(&self) -> *const core::ffi::c_char {
178 self.as_string().as_c_str()
179 }
180
181 fn capacity(&self) -> usize {
183 CAPACITY
184 }
185
186 fn find(&self, bytes: &[u8]) -> Option<usize> {
189 self.as_string().find(bytes)
190 }
191
192 fn rfind(&self, bytes: &[u8]) -> Option<usize> {
195 self.as_string().find(bytes)
196 }
197
198 fn is_full(&self) -> bool {
200 self.as_string().is_full()
201 }
202
203 fn is_empty(&self) -> bool {
205 self.as_string().is_empty()
206 }
207
208 fn len(&self) -> usize {
210 self.as_string().len()
211 }
212
213 fn insert(&mut self, idx: usize, byte: u8) -> Result<(), SemanticStringError> {
216 self.insert_bytes(idx, &[byte; 1])
217 }
218
219 fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) -> Result<(), SemanticStringError> {
222 let msg = "Unable to insert byte string";
223 fail!(from self, when unsafe { self.get_mut_string().insert_bytes(idx, bytes) },
224 with SemanticStringError::ExceedsMaximumLength,
225 "{} \"{}\" since it would exceed the maximum allowed length of {}.",
226 msg, as_escaped_string(bytes), CAPACITY);
227
228 if Self::is_invalid_content(self.as_bytes()) {
229 unsafe { self.get_mut_string().remove_range(idx, bytes.len()) };
230 fail!(from self, with SemanticStringError::InvalidContent,
231 "{} \"{}\" since it would result in an illegal content.",
232 msg, as_escaped_string(bytes));
233 }
234
235 Ok(())
236 }
237
238 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]);
249
250 fn normalize(&self) -> Self;
255
256 fn pop(&mut self) -> Result<Option<u8>, SemanticStringError> {
259 if self.len() == 0 {
260 return Ok(None);
261 }
262
263 self.remove(self.len() - 1)
264 }
265
266 fn push(&mut self, byte: u8) -> Result<(), SemanticStringError> {
269 self.insert(self.len(), byte)
270 }
271
272 fn push_bytes(&mut self, bytes: &[u8]) -> Result<(), SemanticStringError> {
275 self.insert_bytes(self.len(), bytes)
276 }
277
278 fn remove(&mut self, idx: usize) -> Result<Option<u8>, SemanticStringError> {
281 let mut temp = *self.as_string();
282 let value = temp.remove(idx);
283
284 if Self::is_invalid_content(temp.as_bytes()) {
285 fail!(from self, with SemanticStringError::InvalidContent,
286 "Unable to remove character at position {} since it would result in an illegal content.",
287 idx);
288 }
289
290 unsafe { *self.get_mut_string() = temp };
291 Ok(value)
292 }
293
294 fn remove_range(&mut self, idx: usize, len: usize) -> Result<(), SemanticStringError> {
297 let mut temp = *self.as_string();
298 temp.remove_range(idx, len);
299 if Self::is_invalid_content(temp.as_bytes()) {
300 fail!(from self, with SemanticStringError::InvalidContent,
301 "Unable to remove range from {} with lenght {} since it would result in the illegal content \"{}\".",
302 idx, len, temp);
303 }
304
305 unsafe { self.get_mut_string().remove_range(idx, len) };
306 Ok(())
307 }
308
309 fn retain<F: FnMut(u8) -> bool>(&mut self, f: F) -> Result<(), SemanticStringError> {
312 let mut temp = *self.as_string();
313 temp.retain(f);
314
315 if Self::is_invalid_content(temp.as_bytes()) {
316 fail!(from self, with SemanticStringError::InvalidContent,
317 "Unable to retain characters from string since it would result in the illegal content \"{}\".",
318 temp);
319 }
320
321 unsafe { *self.get_mut_string() = temp };
322
323 Ok(())
324 }
325
326 fn strip_prefix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
330 let mut temp = *self.as_string();
331 if !temp.strip_prefix(bytes) {
332 return Ok(false);
333 }
334
335 if Self::is_invalid_content(temp.as_bytes()) {
336 let mut prefix = StaticString::<123>::new();
337 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
338 fail!(from self, with SemanticStringError::InvalidContent,
339 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
340 prefix, temp);
341 }
342
343 unsafe { self.get_mut_string().strip_prefix(bytes) };
344
345 Ok(true)
346 }
347
348 fn strip_suffix(&mut self, bytes: &[u8]) -> Result<bool, SemanticStringError> {
352 let mut temp = *self.as_string();
353 if !temp.strip_suffix(bytes) {
354 return Ok(false);
355 }
356
357 if Self::is_invalid_content(temp.as_bytes()) {
358 let mut prefix = StaticString::<123>::new();
359 unsafe { prefix.insert_bytes_unchecked(0, bytes) };
360 fail!(from self, with SemanticStringError::InvalidContent,
361 "Unable to strip prefix \"{}\" from string since it would result in the illegal content \"{}\".",
362 prefix, temp);
363 }
364
365 unsafe { self.get_mut_string().strip_suffix(bytes) };
366
367 Ok(true)
368 }
369
370 fn truncate(&mut self, new_len: usize) -> Result<(), SemanticStringError> {
372 let mut temp = *self.as_string();
373 temp.truncate(new_len);
374
375 if Self::is_invalid_content(temp.as_bytes()) {
376 fail!(from self, with SemanticStringError::InvalidContent,
377 "Unable to truncate characters to {} since it would result in the illegal content \"{}\".",
378 new_len, temp);
379 }
380
381 unsafe { self.get_mut_string().truncate(new_len) };
382 Ok(())
383 }
384}
385
386#[macro_export(local_inner_macros)]
389macro_rules! semantic_string {
390 {$(#[$documentation:meta])*
391 name: $string_name:ident,
393 capacity: $capacity:expr,
395 invalid_content: $invalid_content:expr,
398 invalid_characters: $invalid_characters:expr,
401 normalize: $normalize:expr} => {
404 $(#[$documentation])*
405 #[repr(C)]
406 #[derive(Debug, Clone, Copy, Eq, PartialOrd, Ord, ZeroCopySend)]
407 pub struct $string_name {
408 value: iceoryx2_bb_container::string::StaticString<$capacity>
409 }
410
411 pub(crate) mod VisitorType {
413 pub(crate) struct $string_name;
414 }
415
416 impl<'de> serde::de::Visitor<'de> for VisitorType::$string_name {
417 type Value = $string_name;
418
419 fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result {
420 formatter.write_str("a string containing the service name")
421 }
422
423 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
424 where
425 E: serde::de::Error,
426 {
427 match $string_name::new(v.as_bytes()) {
428 Ok(v) => Ok(v),
429 Err(v) => Err(E::custom(alloc::format!("invalid {} provided {:?}.", core::stringify!($string_name), v))),
430 }
431 }
432 }
433
434 impl<'de> serde::Deserialize<'de> for $string_name {
435 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
436 where
437 D: serde::Deserializer<'de>,
438 {
439 deserializer.deserialize_str(VisitorType::$string_name)
440 }
441 }
442
443 impl serde::Serialize for $string_name {
444 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
445 where
446 S: serde::Serializer,
447 {
448 serializer.serialize_str(core::str::from_utf8(self.as_bytes()).unwrap())
449 }
450 }
451 impl iceoryx2_bb_container::semantic_string::SemanticString<$capacity> for $string_name {
454 fn as_string(&self) -> &iceoryx2_bb_container::string::StaticString<$capacity> {
455 &self.value
456 }
457
458 fn normalize(&self) -> Self {
459 $normalize(self)
460 }
461
462 unsafe fn new_unchecked(bytes: &[u8]) -> Self {
463 Self {
464 value: iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(bytes),
465 }
466 }
467
468 unsafe fn insert_bytes_unchecked(&mut self, idx: usize, bytes: &[u8]) {
469 use iceoryx2_bb_container::string::String;
470 self.value.insert_bytes_unchecked(idx, bytes);
471 }
472 }
473
474 impl $string_name {
475 pub const unsafe fn new_unchecked_const(value: &[u8]) -> $string_name {
483 core::debug_assert!(value.len() <= $capacity);
484 $string_name {
485 value: iceoryx2_bb_container::string::StaticString::from_bytes_unchecked(value),
486 }
487 }
488
489 pub const fn max_len() -> usize {
491 $capacity
492 }
493
494 pub const fn as_bytes_const(&self) -> &[u8] {
495 self.value.as_bytes_const()
496 }
497 }
498
499 impl core::fmt::Display for $string_name {
500 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
501 core::write!(f, "{}", self.value)
502 }
503 }
504
505 impl Hash for $string_name {
506 fn hash<H: Hasher>(&self, state: &mut H) {
507 self.normalize().as_bytes().hash(state)
508 }
509 }
510
511 impl From<$string_name> for String {
512 fn from(value: $string_name) -> String {
513 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
515 }
516 }
517
518 impl From<&$string_name> for String {
519 fn from(value: &$string_name) -> String {
520 unsafe { String::from_utf8_unchecked(value.as_bytes().to_vec()) }
522 }
523 }
524
525 impl core::convert::TryFrom<&str> for $string_name {
526 type Error = iceoryx2_bb_container::semantic_string::SemanticStringError;
527
528 fn try_from(value: &str) -> Result<Self, Self::Error> {
529 Self::new(value.as_bytes())
530 }
531 }
532
533 impl PartialEq<$string_name> for $string_name {
534 fn eq(&self, other: &$string_name) -> bool {
535 *self.normalize().as_bytes() == *other.normalize().as_bytes()
536 }
537 }
538
539 impl PartialEq<&[u8]> for $string_name {
540 fn eq(&self, other: &&[u8]) -> bool {
541 let other = match $string_name::new(other) {
542 Ok(other) => other,
543 Err(_) => return false,
544 };
545
546 *self == other
547 }
548 }
549
550 impl PartialEq<&[u8]> for &$string_name {
551 fn eq(&self, other: &&[u8]) -> bool {
552 let other = match $string_name::new(other) {
553 Ok(other) => other,
554 Err(_) => return false,
555 };
556
557 **self == other
558 }
559 }
560
561 impl<const CAPACITY: usize> PartialEq<[u8; CAPACITY]> for $string_name {
562 fn eq(&self, other: &[u8; CAPACITY]) -> bool {
563 let other = match $string_name::new(other) {
564 Ok(other) => other,
565 Err(_) => return false,
566 };
567
568 *self == other
569 }
570 }
571
572 impl<const CAPACITY: usize> PartialEq<&[u8; CAPACITY]> for $string_name {
573 fn eq(&self, other: &&[u8; CAPACITY]) -> bool {
574 let other = match $string_name::new(*other) {
575 Ok(other) => other,
576 Err(_) => return false,
577 };
578
579 *self == other
580 }
581 }
582
583 impl PartialEq<&str> for &$string_name {
584 fn eq(&self, other: &&str) -> bool {
585 let other = match $string_name::new(other.as_bytes()) {
586 Ok(other) => other,
587 Err(_) => return false,
588 };
589
590 **self == other
591 }
592 }
593
594 impl core::ops::Deref for $string_name {
595 type Target = [u8];
596
597 fn deref(&self) -> &Self::Target {
598 use iceoryx2_bb_container::string::String;
599 self.value.as_bytes()
600 }
601 }
602
603 impl iceoryx2_bb_container::semantic_string::internal::SemanticStringAccessor<$capacity> for $string_name {
604 unsafe fn new_empty() -> Self {
605 Self {
606 value: iceoryx2_bb_container::string::StaticString::new(),
607 }
608 }
609
610 unsafe fn get_mut_string(&mut self) -> &mut iceoryx2_bb_container::string::StaticString<$capacity> {
611 &mut self.value
612 }
613
614 fn is_invalid_content(string: &[u8]) -> bool {
615 if Self::does_contain_invalid_characters(string) {
616 return true;
617 }
618
619 $invalid_content(string)
620 }
621
622 fn does_contain_invalid_characters(string: &[u8]) -> bool {
623 if core::str::from_utf8(string).is_err() {
624 return true;
625 }
626
627 $invalid_characters(string)
628 }
629 }
630
631 };
632}