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::os::raw::c_char;
9
10use crate::unsafe_send::Sendable;
11
12/// String wrapper for OBS function calls.
13///
14/// `ObsString` provides safe interaction with OBS C API functions that require 
15/// C-style strings. It wraps `CString` internally with convenient helper functions
16/// for converting between Rust strings and C-compatible strings.
17///
18/// # Safety
19///
20/// - Any NUL byte in input strings is stripped during conversion to prevent panicking
21/// - Memory is properly managed to prevent use-after-free and memory leaks
22/// - Automatically handles conversion between Rust's UTF-8 strings and C's NUL-terminated strings
23///
24/// # Examples
25///
26/// ```
27/// use libobs_wrapper::utils::ObsString;
28///
29/// // Create an ObsString from a Rust string
30/// let obs_string = ObsString::new("Hello, OBS!");
31///
32/// // Use in OBS API calls
33/// unsafe {
34///     let ptr = obs_string.as_ptr();
35///     // Pass ptr.0 to OBS functions
36/// }
37/// ```
38#[derive(Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord)]
39pub struct ObsString {
40    /// The underlying C string representation
41    c_string: CString,
42}
43
44impl ObsString {
45    /// Creates a new `ObsString` from a string slice.
46    ///
47    /// Any NUL bytes in the input are automatically stripped to prevent
48    /// panicking when converting to a C string.
49    ///
50    /// # Examples
51    ///
52    /// ```
53    /// use libobs_wrapper::utils::ObsString;
54    ///
55    /// let obs_string = ObsString::new("source_name");
56    /// ```
57    pub fn new<S: AsRef<str>>(s: S) -> Self {
58        let s = s.as_ref().replace("\0", "");
59        Self {
60            c_string: CString::new(s).unwrap(),
61        }
62    }
63
64    /// Returns a pointer to the underlying C string along with sendable wrapper.
65    ///
66    /// The returned pointer is suitable for passing to OBS C API functions.
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use libobs_wrapper::utils::ObsString;
72    ///
73    /// let obs_string = ObsString::new("source_name");
74    /// let ptr = obs_string.as_ptr();
75    ///
76    /// // Use ptr.0 in OBS API calls
77    /// ```
78    pub fn as_ptr(&self) -> Sendable<*const c_char> {
79        Sendable(self.c_string.as_ptr())
80    }
81}
82
83impl ToString for ObsString {
84    /// Converts the `ObsString` back to a Rust `String`.
85    ///
86    /// # Examples
87    ///
88    /// ```
89    /// use libobs_wrapper::utils::ObsString;
90    ///
91    /// let obs_string = ObsString::new("Hello");
92    /// assert_eq!(obs_string.to_string(), "Hello");
93    /// ```
94    fn to_string(&self) -> String {
95        self.c_string.to_string_lossy().into_owned()
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}