mirror_common/strings.rs
1use std::{
2 ffi::{c_char, CStr, CString},
3 str::Utf8Error,
4};
5
6use thiserror::Error;
7
8#[derive(Debug, Error)]
9pub enum StringError {
10 #[error(transparent)]
11 Utf8Error(#[from] Utf8Error),
12 #[error("the string ptr is null")]
13 Null,
14}
15
16/// A type representing an owned, C-compatible, nul-terminated string with no
17/// nul bytes in the middle.
18///
19/// This type serves the purpose of being able to safely generate a C-compatible
20/// string from a Rust byte slice or vector. An instance of this type is a
21/// static guarantee that the underlying bytes contain no interior 0 bytes (“nul
22/// characters”) and that the final byte is 0 (“nul terminator”).
23///
24/// CString is to &CStr as String is to &str: the former in each pair are owned
25/// strings; the latter are borrowed references.
26pub struct Strings {
27 ptr: *const c_char,
28 drop: bool,
29}
30
31impl Drop for Strings {
32 fn drop(&mut self) {
33 if self.drop && !self.ptr.is_null() {
34 drop(unsafe { CString::from_raw(self.ptr as *mut c_char) })
35 }
36 }
37}
38
39impl From<*const c_char> for Strings {
40 fn from(ptr: *const c_char) -> Self {
41 Self { drop: false, ptr }
42 }
43}
44
45impl From<&str> for Strings {
46 fn from(value: &str) -> Self {
47 Self {
48 ptr: CString::new(value).unwrap().into_raw(),
49 drop: true,
50 }
51 }
52}
53
54impl From<String> for Strings {
55 fn from(value: String) -> Self {
56 Self {
57 ptr: CString::new(value).unwrap().into_raw(),
58 drop: true,
59 }
60 }
61}
62
63impl Strings {
64 /// Yields a &str slice if the CStr contains valid UTF-8.
65 ///
66 /// If the contents of the CStr are valid UTF-8 data, this function will
67 /// return the corresponding &str slice. Otherwise, it will return an error
68 /// with details of where UTF-8 validation failed.
69 pub fn to_string(&self) -> Result<String, StringError> {
70 if !self.ptr.is_null() {
71 Ok(unsafe { CStr::from_ptr(self.ptr) }
72 .to_str()
73 .map(|s| s.to_string())?)
74 } else {
75 Err(StringError::Null)
76 }
77 }
78
79 /// Returns the inner pointer to this C string.
80 ///
81 ///The returned pointer will be valid for as long as self is, and points to
82 /// a contiguous region of memory terminated with a 0 byte to represent the
83 /// end of the string.
84 ///
85 ///The type of the returned pointer is *const c_char, and whether it’s an
86 /// alias for *const i8 or *const u8 is platform-specific.
87 ///
88 /// ### WARNING
89 ///
90 ///The returned pointer is read-only; writing to it (including passing it
91 /// to C code that writes to it) causes undefined behavior.
92 pub fn as_ptr(&self) -> *const c_char {
93 self.ptr
94 }
95}
96
97#[macro_export]
98macro_rules! c_str {
99 ($s:expr) => {
100 mirror_common::strings::Strings::from($s).as_ptr()
101 };
102}