hylarana_common/
strings.rs

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