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}