libobs_wrapper\utils/
obs_string.rs

1//! String handling utilities for OBS API integration
2//!
3//! This module provides safe string handling between Rust and the OBS C API.
4//! The core type `ObsString` wraps C-compatible strings in a memory-safe way,
5//! ensuring proper lifetime management and UTF-8 validation.
6
7use std::ffi::CString;
8use std::fmt;
9use std::os::raw::c_char;
10
11use crate::unsafe_send::Sendable;
12
13/// String wrapper for OBS function calls.
14///
15/// `ObsString` provides safe interaction with OBS C API functions that require
16/// C-style strings. It wraps `CString` internally with convenient helper functions
17/// for converting between Rust strings and C-compatible strings.
18///
19/// # Safety
20///
21/// - Any NUL byte in input strings is stripped during conversion to prevent panicking
22/// - Memory is properly managed to prevent use-after-free and memory leaks
23/// - Automatically handles conversion between Rust's UTF-8 strings and C's NUL-terminated strings
24///
25/// # Examples
26///
27/// ```
28/// use libobs_wrapper::utils::ObsString;
29///
30/// // Create an ObsString from a Rust string
31/// let obs_string = ObsString::new("Hello, OBS!");
32///
33/// // Use in OBS API calls
34/// unsafe {
35///     let ptr = obs_string.as_ptr();
36///     // Pass ptr.0 to OBS functions
37/// }
38/// ```
39#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
40pub struct ObsString {
41    /// The underlying C string representation
42    c_string: CString,
43}
44
45impl ObsString {
46    /// Creates a new `ObsString` from a string slice.
47    ///
48    /// Any NUL bytes in the input are automatically stripped to prevent
49    /// panicking when converting to a C string.
50    ///
51    /// # Examples
52    ///
53    /// ```
54    /// use libobs_wrapper::utils::ObsString;
55    ///
56    /// let obs_string = ObsString::new("source_name");
57    /// ```
58    pub fn new<S: AsRef<str>>(s: S) -> Self {
59        let s = s.as_ref().replace("\0", "");
60        Self {
61            c_string: CString::new(s).unwrap(),
62        }
63    }
64
65    /// Returns a pointer to the underlying C string along with sendable wrapper.
66    ///
67    /// The returned pointer is suitable for passing to OBS C API functions.
68    ///
69    /// # Examples
70    ///
71    /// ```
72    /// use libobs_wrapper::utils::ObsString;
73    ///
74    /// let obs_string = ObsString::new("source_name");
75    /// let ptr = obs_string.as_ptr();
76    ///
77    /// // Use ptr.0 in OBS API calls
78    /// ```
79    pub fn as_ptr(&self) -> Sendable<*const c_char> {
80        Sendable(self.c_string.as_ptr())
81    }
82}
83impl fmt::Display for ObsString {
84    /// Converts the `ObsString` back to a Rust `String` for display.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use libobs_wrapper::utils::ObsString;
90    ///
91    /// let obs_string = ObsString::new("Hello");
92    /// assert_eq!(format!("{}", obs_string), "Hello");
93    /// ```
94    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95        write!(f, "{}", self.c_string.to_string_lossy())
96    }
97}
98
99impl From<&str> for ObsString {
100    /// Creates an `ObsString` from a string slice.
101    ///
102    /// # Examples
103    ///
104    /// ```
105    /// use libobs_wrapper::utils::ObsString;
106    ///
107    /// let obs_string: ObsString = "Hello".into();
108    /// ```
109    fn from(value: &str) -> Self {
110        let value = value.replace("\0", "");
111        Self {
112            c_string: CString::new(value).unwrap(),
113        }
114    }
115}
116
117impl From<Vec<u8>> for ObsString {
118    /// Creates an `ObsString` from a vector of bytes.
119    ///
120    /// Any NUL bytes in the input are automatically filtered out.
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use libobs_wrapper::utils::ObsString;
126    ///
127    /// let bytes = b"Hello".to_vec();
128    /// let obs_string: ObsString = bytes.into();
129    /// ```
130    fn from(mut value: Vec<u8>) -> Self {
131        value.retain(|&c| c != 0);
132        Self {
133            c_string: CString::new(value).unwrap(),
134        }
135    }
136}
137
138impl From<String> for ObsString {
139    /// Creates an `ObsString` from a `String`.
140    ///
141    /// # Examples
142    ///
143    /// ```
144    /// use libobs_wrapper::utils::ObsString;
145    ///
146    /// let s = String::from("Hello");
147    /// let obs_string: ObsString = s.into();
148    /// ```
149    fn from(value: String) -> Self {
150        let value = value.replace("\0", "");
151        Self {
152            c_string: CString::new(value).unwrap(),
153        }
154    }
155}