We have these:
/// Strips attributes from text. See [`Strip`] for which attributes can
/// be stripped.
///
/// # Examples
///
/// ```no_run
/// use hexchat_unsafe_plugin::{PluginHandle, Strip};
///
/// /// Removes colors
/// fn strip_colors(ph: &PluginHandle<'_>, s: &str) -> String {
/// ph.strip(s, Strip::new().colors(true))
/// }
/// ```
#[cfg(feature = "nul_in_strip")]
pub fn strip(&self, s: &str, strip: Strip) -> String {
let ph = self.ph;
// ironically the single API where we can pass embedded NULs.
// we also don't need to worry about terminating it with NUL.
let mut out = Vec::with_capacity(s.len());
let in_ptr = s.as_ptr() as *const _;
let in_len = s.len().try_into().unwrap();
let flags = strip.flags();
// NOTE: avoid panicking from here.
let stripped = unsafe {
((*ph.ph).hexchat_strip)(ph.ph, in_ptr, in_len, flags)
};
// tho annoyingly we don't know the out len, so we need to count NULs.
let in_nuls = s.as_bytes().into_iter().filter(|&&x| x == 0).count();
let mut out_len = 0;
for _ in 0..=in_nuls {
while unsafe { *stripped.add(out_len) } != 0 {
out_len += 1;
}
out_len += 1;
}
out.extend_from_slice(unsafe {
// take out the extra NUL at the end.
::std::slice::from_raw_parts(stripped as *const _, out_len - 1)
});
unsafe {
((*ph.ph).hexchat_free)(ph.ph, stripped as *const _);
}
// we can panic again here, tho this should never panic.
String::from_utf8(out).unwrap()
}
/// Strips attributes from text. See [`Strip`] for which attributes can
/// be stripped.
///
/// # Examples
///
/// ```no_run
/// use hexchat_unsafe_plugin::{PluginHandle, Strip};
///
/// /// Removes colors
/// fn strip_colors(ph: &PluginHandle<'_>, s: &str) -> String {
/// ph.strip(s, Strip::new().colors(true))
/// }
/// ```
#[cfg(not(feature = "nul_in_strip"))]
pub fn strip(&self, s: &str, strip: Strip) -> String {
let ph = self.ph;
let s = CString::new(s).unwrap();
let flags = strip.flags();
// NOTE: avoid panicking from here.
let stripped = unsafe {
((*ph.ph).hexchat_strip)(ph.ph, s.as_ptr(), -1, flags)
};
let mut s = s.into_string().unwrap_or_else(|_| unsafe {
std::hint::unreachable_unchecked()
});
if let Ok(stripped) = unsafe { CStr::from_ptr(stripped) }.to_str() {
s.clear();
s.push_str(stripped);
} else {
unsafe {
((*ph.ph).hexchat_free)(ph.ph, stripped as *const _);
}
unreachable!("broken hexchat");
}
unsafe {
((*ph.ph).hexchat_free)(ph.ph, stripped as *const _);
}
s
}
Which one should we keep?