osal_rs/utils.rs
1/***************************************************************************
2 *
3 * osal-rs
4 * Copyright (C) 2023/2026 Antonio Salsi <passy.linux@zresa.it>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 ***************************************************************************/
19
20//! Utility types and functions for OSAL-RS.
21//!
22//! This module contains common types, error definitions, and helper functions
23//! used throughout the library.
24
25use core::ffi::{CStr, c_char};
26use core::{ffi::c_void, str::from_utf8_mut};
27use core::fmt::{Debug, Display};
28use core::ops::{Deref, DerefMut};
29use core::time::Duration;
30use alloc::ffi::CString;
31use alloc::string::{String, ToString};
32use alloc::sync::Arc;
33
34use crate::os::Mutex;
35
36/// Error types for OSAL-RS operations.
37///
38/// Represents all possible error conditions that can occur when using
39/// the OSAL-RS library.
40///
41/// # Examples
42///
43/// ```ignore
44/// use osal_rs::os::{Queue, QueueFn};
45/// use osal_rs::utils::Error;
46///
47/// match Queue::new(10, 32) {
48/// Ok(queue) => { /* use queue */ },
49/// Err(Error::OutOfMemory) => println!("Failed to allocate queue"),
50/// Err(e) => println!("Other error: {:?}", e),
51/// }
52/// ```
53#[derive(Debug, Clone, PartialEq, Eq, Hash)]
54pub enum Error {
55 /// Insufficient memory to complete operation
56 OutOfMemory,
57 /// Queue send operation timed out
58 QueueSendTimeout,
59 /// Queue receive operation timed out
60 QueueReceiveTimeout,
61 /// Mutex operation timed out
62 MutexTimeout,
63 /// Failed to acquire mutex lock
64 MutexLockFailed,
65 /// Generic timeout error
66 Timeout,
67 /// Queue is full and cannot accept more items
68 QueueFull,
69 /// String conversion failed
70 StringConversionError,
71 /// Thread/task not found
72 TaskNotFound,
73 /// Invalid queue size specified
74 InvalidQueueSize,
75 /// Null pointer encountered
76 NullPtr,
77 /// Requested item not found
78 NotFound,
79 /// Index out of bounds
80 OutOfIndex,
81 /// Invalid type for operation
82 InvalidType,
83 /// No data available
84 Empty,
85 /// Write error occurred
86 WriteError,
87 /// Read error occurred
88 ReadError,
89 /// Return error with code
90 ReturnWithCode(i32),
91 /// Unhandled error with description
92 Unhandled(&'static str)
93}
94
95/// CPU register size enumeration.
96///
97/// Identifies whether the target CPU uses 32-bit or 64-bit registers.
98/// This is used for platform-specific tick count overflow handling.
99#[derive(PartialEq, Eq, Clone, Copy, Debug)]
100pub enum CpuRegisterSize {
101 /// 64-bit CPU registers
102 Bit64,
103 /// 32-bit CPU registers
104 Bit32
105}
106
107/// Boolean type compatible with RTOS return values.
108///
109/// Many RTOS functions return 0 for success and non-zero for failure.
110/// This type provides a Rust-idiomatic way to work with such values.
111///
112/// # Examples
113///
114/// ```ignore
115/// use osal_rs::os::{Semaphore, SemaphoreFn};
116/// use osal_rs::utils::OsalRsBool;
117/// use core::time::Duration;
118///
119/// let sem = Semaphore::new(1, 1).unwrap();
120///
121/// match sem.wait(Duration::from_millis(100)) {
122/// OsalRsBool::True => println!("Acquired semaphore"),
123/// OsalRsBool::False => println!("Failed to acquire"),
124/// }
125///
126/// // Can also convert to bool
127/// if sem.signal().into() {
128/// println!("Semaphore signaled");
129/// }
130/// ```
131#[derive(PartialEq, Eq, Clone, Copy, Debug)]
132#[repr(u8)]
133pub enum OsalRsBool {
134 /// Operation failed or condition is false
135 False = 1,
136 /// Operation succeeded or condition is true
137 True = 0
138}
139
140/// Maximum delay constant for blocking operations.
141///
142/// When used as a timeout parameter, indicates the operation should
143/// block indefinitely until it succeeds.
144///
145/// # Examples
146///
147/// ```ignore
148/// use osal_rs::os::{Mutex, MutexFn};
149/// use osal_rs::utils::MAX_DELAY;
150///
151/// let mutex = Mutex::new(0);
152/// let guard = mutex.lock(); // Blocks forever if needed
153/// ```
154pub const MAX_DELAY: Duration = Duration::from_millis(usize::MAX as u64);
155
156/// Standard Result type for OSAL-RS operations.
157///
158/// Uses [`Error`] as the default error type.
159pub type Result<T, E = Error> = core::result::Result<T, E>;
160
161/// Pointer to pointer type for C FFI.
162pub type DoublePtr = *mut *mut c_void;
163
164/// Mutable pointer type for C FFI.
165pub type Ptr = *mut c_void;
166
167/// Const pointer type for C FFI.
168pub type ConstPtr = *const c_void;
169
170/// Shortcut for Arc<Mutex<T>>
171pub type ArcMux<T> = Arc<Mutex<T>>;
172
173/// Determines the CPU register size at compile time.
174///
175/// This constant function checks the size of `usize` to determine whether
176/// the target architecture uses 32-bit or 64-bit registers. This information
177/// is used for platform-specific optimizations and overflow handling.
178///
179/// # Returns
180///
181/// * [`CpuRegisterSize::Bit64`] - For 64-bit architectures
182/// * [`CpuRegisterSize::Bit32`] - For 32-bit architectures
183///
184/// # Examples
185///
186/// ```ignore
187/// use osal_rs::utils::{register_bit_size, CpuRegisterSize};
188///
189/// match register_bit_size() {
190/// CpuRegisterSize::Bit64 => println!("Running on 64-bit platform"),
191/// CpuRegisterSize::Bit32 => println!("Running on 32-bit platform"),
192/// }
193/// ```
194pub const fn register_bit_size() -> CpuRegisterSize {
195 if size_of::<usize>() == 8 {
196 CpuRegisterSize::Bit64
197 } else {
198 CpuRegisterSize::Bit32
199 }
200}
201
202/// Converts a C string pointer to a Rust String.
203///
204/// This macro safely converts a raw C string pointer (`*const c_char`) into
205/// a Rust `String`. It handles UTF-8 conversion gracefully using lossy conversion.
206///
207/// # Safety
208///
209/// The pointer must be valid and point to a null-terminated C string.
210///
211/// # Examples
212///
213/// ```ignore
214/// use osal_rs::from_c_str;
215/// use core::ffi::c_char;
216///
217/// extern "C" {
218/// fn get_system_name() -> *const c_char;
219/// }
220///
221/// let name = from_c_str!(get_system_name());
222/// println!("System: {}", name);
223/// ```
224#[macro_export]
225macro_rules! from_c_str {
226 ($str:expr) => {
227 unsafe {
228 let c_str = core::ffi::CStr::from_ptr($str);
229 alloc::string::String::from_utf8_lossy(c_str.to_bytes()).to_string()
230 }
231 };
232}
233
234/// Converts a Rust string to a CString with error handling.
235///
236/// This macro creates a `CString` from a Rust string reference, returning
237/// a `Result` that can be used with the `?` operator. If the conversion fails
238/// (e.g., due to interior null bytes), it returns an appropriate error.
239///
240/// # Returns
241///
242/// * `Ok(CString)` - On successful conversion
243/// * `Err(Error::Unhandled)` - If the string contains interior null bytes
244///
245/// # Examples
246///
247/// ```ignore
248/// use osal_rs::to_cstring;
249/// use osal_rs::utils::Result;
250///
251/// fn pass_to_c_api(name: &str) -> Result<()> {
252/// let c_name = to_cstring!(name)?;
253/// // Use c_name.as_ptr() with C FFI
254/// Ok(())
255/// }
256/// ```
257#[macro_export]
258macro_rules! to_cstring {
259 ($s:expr) => {
260 alloc::ffi::CString::new($s.as_str())
261 .map_err(|_| $crate::utils::Error::Unhandled("Failed to convert string to CString"))
262 };
263}
264
265/// Converts a Rust string to a C string pointer.
266///
267/// This macro creates a `CString` from a Rust string and returns its raw pointer.
268/// **Warning**: This macro panics if the conversion fails. Consider using
269/// [`to_cstring!`] for safer error handling.
270///
271/// # Panics
272///
273/// Panics if the string contains interior null bytes.
274///
275/// # Examples
276///
277/// ```ignore
278/// use osal_rs::to_c_str;
279///
280/// extern "C" {
281/// fn set_name(name: *const core::ffi::c_char);
282/// }
283///
284/// let name = "FreeRTOS Task";
285/// unsafe {
286/// set_name(to_c_str!(name));
287/// }
288/// ```
289#[macro_export]
290macro_rules! to_c_str {
291 ($s:expr) => {
292 alloc::ffi::CString::new($s.as_ref() as &str).unwrap().as_ptr()
293 };
294}
295
296/// Converts a string to a fixed-size byte array.
297///
298/// This macro creates a byte array of the specified size and fills it with
299/// the bytes from the input string. If the string is shorter than the buffer,
300/// the remaining bytes are filled with spaces. If the string is longer, it
301/// is truncated to fit.
302///
303/// # Parameters
304///
305/// * `$str` - The source string to convert
306/// * `$buff_name` - The identifier name for the created buffer variable
307/// * `$buff_size` - The size of the byte array to create
308///
309/// # Examples
310///
311/// ```ignore
312/// use osal_rs::from_str_to_array;
313///
314/// let task_name = "MainTask";
315/// from_str_to_array!(task_name, name_buffer, 16);
316/// // name_buffer is now [u8; 16] containing "MainTask "
317///
318/// // Use with C FFI
319/// extern "C" {
320/// fn create_task(name: *const u8, len: usize);
321/// }
322///
323/// unsafe {
324/// create_task(name_buffer.as_ptr(), name_buffer.len());
325/// }
326/// ```
327#[macro_export]
328macro_rules! from_str_to_array {
329 ($str:expr, $buff_name:ident, $buff_size:expr) => {
330 let mut $buff_name = [b' '; $buff_size];
331 let _bytes = $str.as_bytes();
332 let _len = core::cmp::min(_bytes.len(), $buff_size);
333 $buff_name[.._len].copy_from_slice(&_bytes[.._len]);
334 };
335}
336
337/// Extracts a typed parameter from an optional boxed Any reference.
338///
339/// This macro is used in thread/task entry points to safely extract and
340/// downcast parameters passed to the thread. It handles both the Option
341/// unwrapping and the type downcast, returning appropriate errors if either
342/// operation fails.
343///
344/// # Parameters
345///
346/// * `$param` - An `Option<Box<dyn Any>>` containing the parameter
347/// * `$t` - The type to downcast the parameter to
348///
349/// # Returns
350///
351/// * A reference to the downcasted value of type `$t`
352/// * `Err(Error::NullPtr)` - If the parameter is None
353/// * `Err(Error::InvalidType)` - If the downcast fails
354///
355/// # Examples
356///
357/// ```ignore
358/// use osal_rs::thread_extract_param;
359/// use osal_rs::utils::Result;
360/// use core::any::Any;
361///
362/// struct TaskConfig {
363/// priority: u8,
364/// stack_size: usize,
365/// }
366///
367/// fn task_entry(param: Option<Box<dyn Any>>) -> Result<()> {
368/// let config = thread_extract_param!(param, TaskConfig);
369///
370/// println!("Priority: {}", config.priority);
371/// println!("Stack: {}", config.stack_size);
372///
373/// Ok(())
374/// }
375/// ```
376#[macro_export]
377macro_rules! thread_extract_param {
378 ($param:expr, $t:ty) => {
379 match $param.as_ref() {
380 Some(p) => {
381 match p.downcast_ref::<$t>() {
382 Some(value) => value,
383 None => return Err($crate::utils::Error::InvalidType),
384 }
385 }
386 None => return Err($crate::utils::Error::NullPtr),
387 }
388 };
389}
390
391/// Creates an Arc<Mutex<T>> from a value.
392///
393/// This is a convenience macro to reduce boilerplate when creating
394/// thread-safe shared data structures.
395///
396/// # Examples
397///
398/// ```ignore
399/// use osal_rs::arcmux;
400///
401/// let shared_counter = arcmux!(0);
402/// // Equivalent to: Arc::new(Mutex::new(0))
403/// ```
404#[macro_export]
405macro_rules! arcmux {
406 ($value:expr) => {
407 alloc::sync::Arc::new($crate::os::Mutex::new($value))
408 };
409}
410
411
412
413/// Fixed-size byte array wrapper with string conversion utilities.
414///
415/// `Bytes` is a generic wrapper around a fixed-size byte array that provides
416/// convenient methods for converting between strings and byte arrays. It's
417/// particularly useful for interfacing with C APIs that expect fixed-size
418/// character buffers, or for storing strings in embedded systems with
419/// constrained memory.
420///
421/// # Type Parameters
422///
423/// * `SIZE` - The size of the internal byte array (default: 0)
424///
425/// # Examples
426///
427/// ```ignore
428/// use osal_rs::utils::Bytes;
429///
430/// // Create an empty 32-byte buffer
431/// let mut buffer = Bytes::<32>::new();
432///
433/// // Create a buffer from a string
434/// let name = Bytes::<16>::new_by_str("TaskName");
435/// println!("{}", name); // Prints "TaskName"
436///
437/// // Create from any type that implements ToString
438/// let number = 42;
439/// let num_bytes = Bytes::<8>::new_by_string(&number);
440/// ```
441#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
442pub struct Bytes<const SIZE: usize = 0> (pub [u8; SIZE]);
443
444impl<const SIZE: usize> Deref for Bytes<SIZE> {
445 type Target = [u8; SIZE];
446
447 /// Dereferences to the underlying byte array.
448 ///
449 /// This allows `Bytes` to be used anywhere a `[u8; SIZE]` reference is expected.
450 ///
451 /// # Examples
452 ///
453 /// ```ignore
454 /// use osal_rs::utils::Bytes;
455 ///
456 /// let bytes = Bytes::<8>::new_by_str("test");
457 /// assert_eq!(bytes[0], b't');
458 /// ```
459 fn deref(&self) -> &Self::Target {
460 &self.0
461 }
462}
463
464impl<const SIZE: usize> DerefMut for Bytes<SIZE> {
465 /// Provides mutable access to the underlying byte array.
466 ///
467 /// This allows `Bytes` to be mutably dereferenced, enabling direct modification
468 /// of the internal byte array through the `DerefMut` trait.
469 ///
470 /// # Examples
471 ///
472 /// ```ignore
473 /// use osal_rs::utils::Bytes;
474 ///
475 /// let mut bytes = Bytes::<8>::new();
476 /// bytes[0] = b'H';
477 /// bytes[1] = b'i';
478 /// assert_eq!(bytes[0], b'H');
479 /// ```
480 fn deref_mut(&mut self) -> &mut Self::Target {
481 &mut self.0
482 }
483}
484
485impl<const SIZE: usize> Display for Bytes<SIZE> {
486 /// Formats the byte array as a C-style null-terminated string.
487 ///
488 /// This implementation treats the byte array as a C string and converts it
489 /// to a Rust string for display. If the conversion fails, it displays
490 /// "Conversion error".
491 ///
492 /// # Safety
493 ///
494 /// This method assumes the byte array contains valid UTF-8 data and is
495 /// null-terminated. Invalid data may result in the error message being displayed.
496 ///
497 /// # Examples
498 ///
499 /// ```ignore
500 /// use osal_rs::utils::Bytes;
501 ///
502 /// let bytes = Bytes::<16>::new_by_str("Hello");
503 /// println!("{}", bytes); // Prints "Hello"
504 /// ```
505 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
506 let str = unsafe {
507 CStr::from_ptr(self.0.as_ptr() as *const c_char)
508 .to_str()
509 .unwrap_or("Conversion error")
510 };
511
512 write!(f, "{}", str.to_string())
513 }
514}
515
516impl AsSyncStr for Bytes<> {
517 /// Returns a string slice reference.
518 ///
519 /// This method provides access to the underlying string data in a way
520 /// that is safe to use across thread boundaries.
521 ///
522 /// # Returns
523 ///
524 /// A reference to a string slice with lifetime tied to `self`.
525 fn as_str(&self) -> &str {
526 unsafe {
527 CStr::from_ptr(self.0.as_ptr() as *const c_char)
528 .to_str()
529 .unwrap_or("Conversion error")
530 }
531 }
532}
533
534impl<const SIZE: usize> Bytes<SIZE> {
535 /// Creates a new `Bytes` instance filled with zeros.
536 ///
537 /// This is a const function, allowing it to be used in const contexts
538 /// and static variable declarations.
539 ///
540 /// # Returns
541 ///
542 /// A `Bytes` instance with all bytes set to 0.
543 ///
544 /// # Examples
545 ///
546 /// ```ignore
547 /// use osal_rs::utils::Bytes;
548 ///
549 /// const BUFFER: Bytes<64> = Bytes::new();
550 ///
551 /// let runtime_buffer = Bytes::<32>::new();
552 /// assert_eq!(runtime_buffer[0], 0);
553 /// ```
554 pub const fn new() -> Self {
555 Self( [0u8; SIZE] )
556 }
557
558 /// Creates a new `Bytes` instance from a string slice.
559 ///
560 /// Copies the bytes from the input string into the fixed-size array.
561 /// If the string is shorter than `SIZE`, the remaining bytes are zero-filled.
562 /// If the string is longer, it is truncated to fit.
563 ///
564 /// # Parameters
565 ///
566 /// * `str` - The source string to convert
567 ///
568 /// # Returns
569 ///
570 /// A `Bytes` instance containing the string data.
571 ///
572 /// # Examples
573 ///
574 /// ```ignore
575 /// use osal_rs::utils::Bytes;
576 ///
577 /// let short = Bytes::<16>::new_by_str("Hi");
578 /// // Internal array: [b'H', b'i', 0, 0, 0, ...]
579 ///
580 /// let exact = Bytes::<5>::new_by_str("Hello");
581 /// // Internal array: [b'H', b'e', b'l', b'l', b'o']
582 ///
583 /// let long = Bytes::<3>::new_by_str("Hello");
584 /// // Internal array: [b'H', b'e', b'l'] (truncated)
585 /// ```
586 pub fn new_by_str(str: &str) -> Self {
587
588 let mut array = [0u8; SIZE];
589
590 let mut i = 0usize ;
591 for byte in str.as_bytes() {
592 if i > SIZE - 1{
593 break;
594 }
595 array[i] = *byte;
596 i += 1;
597 }
598
599 Self( array )
600 }
601
602 /// Creates a new `Bytes` instance from any type implementing `ToString`.
603 ///
604 /// This is a convenience wrapper around [`new_by_str`](Self::new_by_str)
605 /// that first converts the input to a string.
606 ///
607 /// # Parameters
608 ///
609 /// * `str` - Any value that implements `ToString`
610 ///
611 /// # Returns
612 ///
613 /// A `Bytes` instance containing the string representation of the input.
614 ///
615 /// # Examples
616 ///
617 /// ```ignore
618 /// use osal_rs::utils::Bytes;
619 ///
620 /// // From integer
621 /// let num_bytes = Bytes::<8>::new_by_string(&42);
622 ///
623 /// // From String
624 /// let string = String::from("Task");
625 /// let str_bytes = Bytes::<16>::new_by_string(&string);
626 ///
627 /// // From custom type with ToString
628 /// #[derive(Debug)]
629 /// struct TaskId(u32);
630 /// impl ToString for TaskId {
631 /// fn to_string(&self) -> String {
632 /// format!("Task-{}", self.0)
633 /// }
634 /// }
635 /// let task_bytes = Bytes::<16>::new_by_string(&TaskId(5));
636 /// ```
637 pub fn new_by_string(str: &impl ToString) -> Self {
638 Self::new_by_str(&str.to_string())
639 }
640
641 /// Fills a mutable string slice with the contents of the byte array.
642 ///
643 /// Attempts to convert the internal byte array to a UTF-8 string and
644 /// copies it into the destination string slice. Only copies up to the
645 /// minimum of the source and destination lengths.
646 ///
647 /// # Parameters
648 ///
649 /// * `dest` - The destination string slice to fill
650 ///
651 /// # Panics
652 ///
653 /// Currently panics (todo!) if the byte array contains invalid UTF-8.
654 ///
655 /// # Examples
656 ///
657 /// ```ignore
658 /// use osal_rs::utils::Bytes;
659 ///
660 /// let bytes = Bytes::<16>::new_by_str("Hello World");
661 ///
662 /// let mut output = String::from(" "); // 16 spaces
663 /// bytes.fill_str(unsafe { output.as_mut_str() });
664 ///
665 /// assert_eq!(&output[..11], "Hello World");
666 /// ```
667 pub fn fill_str(&mut self, dest: &mut str) {
668 match from_utf8_mut(&mut self.0) {
669 Ok(str) => {
670 let len = core::cmp::min(str.len(), dest.len());
671 unsafe {
672 dest.as_bytes_mut()[..len].copy_from_slice(&str.as_bytes()[..len]);
673 }
674 }
675 Err(_) => todo!(),
676 }
677 }
678
679 /// Converts the byte array to a C string reference.
680 ///
681 /// Creates a `CStr` reference from the internal byte array, treating it as
682 /// a null-terminated C string. This is useful for passing strings to C FFI
683 /// functions that expect `*const c_char` or `&CStr`.
684 ///
685 /// # Safety
686 ///
687 /// This method is unsafe because it assumes:
688 /// - The byte array contains valid UTF-8 data
689 /// - The byte array is null-terminated
690 /// - There are no interior null bytes before the terminating null
691 ///
692 /// Violating these assumptions may lead to undefined behavior.
693 ///
694 /// # Returns
695 ///
696 /// A reference to a `CStr` with lifetime tied to `self`.
697 ///
698 /// # Examples
699 ///
700 /// ```ignore
701 /// use osal_rs::utils::Bytes;
702 ///
703 /// let bytes = Bytes::<16>::new_by_str("Hello");
704 /// let c_str = bytes.as_c_str();
705 ///
706 /// extern "C" {
707 /// fn print_string(s: *const core::ffi::c_char);
708 /// }
709 ///
710 /// unsafe {
711 /// print_string(c_str.as_ptr());
712 /// }
713 /// ```
714 pub fn as_c_str(&self) -> &CStr {
715 unsafe {
716 CStr::from_ptr(self.0.as_ptr() as *const c_char)
717 }
718 }
719
720 /// Converts the byte array to an owned C string.
721 ///
722 /// Creates a new `CString` by copying the contents of the internal byte array.
723 /// Unlike [`as_c_str`](Self::as_c_str), this method allocates heap memory and
724 /// returns an owned string that can outlive the original `Bytes` instance.
725 ///
726 /// # Safety
727 ///
728 /// This method uses `from_vec_unchecked` which assumes the byte array
729 /// does not contain any interior null bytes. If this assumption is violated,
730 /// the resulting `CString` will be invalid.
731 ///
732 /// # Returns
733 ///
734 /// An owned `CString` containing a copy of the byte array data.
735 ///
736 /// # Memory Allocation
737 ///
738 /// This method allocates on the heap. In memory-constrained embedded systems,
739 /// prefer [`as_c_str`](Self::as_c_str) when possible.
740 ///
741 /// # Examples
742 ///
743 /// ```ignore
744 /// use osal_rs::utils::Bytes;
745 ///
746 /// fn process_name(bytes: &Bytes<16>) -> alloc::ffi::CString {
747 /// // Create an owned copy that can be returned
748 /// bytes.as_cstring()
749 /// }
750 ///
751 /// let name = Bytes::<16>::new_by_str("Task");
752 /// let owned = process_name(&name);
753 /// // 'name' can be dropped, 'owned' still valid
754 /// ```
755 pub fn as_cstring(&self) -> CString {
756 unsafe {
757 CString::from_vec_unchecked(self.0.to_vec())
758 }
759 }
760}
761
762/// Trait for types that can provide a string reference in a thread-safe manner.
763///
764/// This trait extends the basic string reference functionality with thread-safety
765/// guarantees by requiring both `Sync` and `Send` bounds. It's useful for types
766/// that need to provide string data across thread boundaries in a concurrent
767/// environment.
768///
769/// # Thread Safety
770///
771/// Implementors must be both `Sync` (safe to share references across threads) and
772/// `Send` (safe to transfer ownership across threads).
773///
774/// # Examples
775///
776/// ```ignore
777/// use osal_rs::utils::AsSyncStr;
778///
779/// struct ThreadSafeName {
780/// name: &'static str,
781/// }
782///
783/// impl AsSyncStr for ThreadSafeName {
784/// fn as_str(&self) -> &str {
785/// self.name
786/// }
787/// }
788///
789/// // Can be safely shared across threads
790/// fn use_in_thread(item: &dyn AsSyncStr) {
791/// println!("Name: {}", item.as_str());
792/// }
793/// ```
794pub trait AsSyncStr : Sync + Send {
795 /// Returns a string slice reference.
796 ///
797 /// This method provides access to the underlying string data in a way
798 /// that is safe to use across thread boundaries.
799 ///
800 /// # Returns
801 ///
802 /// A reference to a string slice with lifetime tied to `self`.
803 fn as_str(&self) -> &str;
804}
805
806impl PartialEq for dyn AsSyncStr {
807 fn eq(&self, other: &Self) -> bool {
808 self.as_str() == other.as_str()
809 }
810}
811
812impl Eq for dyn AsSyncStr {}
813
814impl Debug for dyn AsSyncStr {
815 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
816 write!(f, "{}", self.as_str())
817 }
818}
819
820impl Display for dyn AsSyncStr {
821 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
822 write!(f, "{}", self.as_str())
823 }
824}
825