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}