null_terminated_str/
owned.rs1use std::{
2 borrow::Borrow,
3 error::Error,
4 ffi::{CStr, CString},
5 fmt,
6 ops::Deref,
7 str::Utf8Error,
8};
9
10use super::NullTerminatedStr;
11
12#[derive(Clone, Debug)]
13pub struct NullStringFromUtf8Error {
14 cstring: CString,
15 utf8_err: Utf8Error,
16}
17
18impl NullStringFromUtf8Error {
19 pub fn into_inner(self) -> (CString, Utf8Error) {
20 (self.cstring, self.utf8_err)
21 }
22}
23
24impl fmt::Display for NullStringFromUtf8Error {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 self.utf8_err.fmt(f)
27 }
28}
29
30impl Error for NullStringFromUtf8Error {
31 fn source(&self) -> Option<&(dyn Error + 'static)> {
32 Some(&self.utf8_err)
33 }
34}
35
36#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
37#[repr(transparent)]
38pub struct NullTerminatedString(CString);
39
40impl NullTerminatedString {
41 pub const unsafe fn from_cstring_unchecked(cstring: CString) -> Self {
45 Self(cstring)
46 }
47
48 pub fn from_cstring(cstring: CString) -> Result<Self, NullStringFromUtf8Error> {
49 if let Err(utf8_err) = NullTerminatedStr::from_cstr(&cstring) {
50 Err(NullStringFromUtf8Error { cstring, utf8_err })
51 } else {
52 Ok(Self(cstring))
53 }
54 }
55
56 pub fn from_cstring_lossy(cstring: CString) -> Self {
57 Self::from_cstring(cstring).unwrap_or_else(|NullStringFromUtf8Error { cstring, .. }| {
58 let bytes = cstring.into_bytes();
60
61 let string = String::from_utf8_lossy(&bytes).into_owned();
65
66 let bytes = string.into_bytes();
68
69 Self(unsafe { CString::from_vec_unchecked(bytes) })
75 })
76 }
77}
78
79impl From<&str> for NullTerminatedString {
80 fn from(s: &str) -> Self {
81 let buf = s.bytes().filter(|byte| *byte != b'\0').collect::<Vec<_>>();
82 Self(unsafe { CString::from_vec_unchecked(buf) })
87 }
88}
89
90impl From<String> for NullTerminatedString {
91 fn from(s: String) -> Self {
92 let mut buf = s.into_bytes();
93 buf.retain(|byte| *byte != b'\0');
94 Self(unsafe { CString::from_vec_unchecked(buf) })
99 }
100}
101
102impl From<NullTerminatedString> for String {
103 fn from(s: NullTerminatedString) -> String {
104 let bytes = s.0.into_bytes();
106 unsafe { String::from_utf8_unchecked(bytes) }
111 }
112}
113
114impl From<NullTerminatedString> for CString {
115 fn from(s: NullTerminatedString) -> CString {
116 s.0
117 }
118}
119
120impl Deref for NullTerminatedString {
121 type Target = NullTerminatedStr;
122
123 fn deref(&self) -> &Self::Target {
124 unsafe { NullTerminatedStr::from_cstr_unchecked(self.0.as_c_str()) }
125 }
126}
127
128impl fmt::Display for NullTerminatedString {
129 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
130 self.deref().fmt(f)
131 }
132}
133
134impl Borrow<NullTerminatedStr> for NullTerminatedString {
135 fn borrow(&self) -> &NullTerminatedStr {
136 self.deref()
137 }
138}
139
140impl AsRef<NullTerminatedStr> for NullTerminatedString {
141 fn as_ref(&self) -> &NullTerminatedStr {
142 self.deref()
143 }
144}
145
146impl AsRef<str> for NullTerminatedString {
147 fn as_ref(&self) -> &str {
148 self.deref().as_ref()
149 }
150}
151
152impl AsRef<CStr> for NullTerminatedString {
153 fn as_ref(&self) -> &CStr {
154 self.deref().as_c_str()
155 }
156}