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}