1use alloc::string::String;
2
3use crate::Error;
4
5pub(crate) trait Sealed {
6 #[cfg(windows)]
7 #[doc(hidden)]
8 fn windows_filename<R>(
9 self,
10 function: impl FnOnce(*const u16) -> Result<R, crate::Error>,
11 ) -> Result<R, crate::Error>;
12
13 #[cfg(any(unix, target_os = "twizzler"))]
14 #[doc(hidden)]
15 fn posix_filename<R>(
16 self,
17 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, crate::Error>,
18 ) -> Result<R, crate::Error>;
19}
20
21#[expect(private_bounds)]
26pub trait AsFilename: Sealed {}
27
28impl AsFilename for &str {}
29impl Sealed for &str {
30 #[cfg(windows)]
31 fn windows_filename<R>(
32 self,
33 function: impl FnOnce(*const u16) -> Result<R, Error>,
34 ) -> Result<R, Error> {
35 let utf16: alloc::vec::Vec<u16> = if crate::util::check_null_bytes(self.as_bytes())? {
36 self.encode_utf16().collect()
37 } else {
38 self.encode_utf16().chain(Some(0)).collect()
39 };
40 function(utf16.as_ptr())
41 }
42
43 #[cfg(any(unix, target_os = "twizzler"))]
44 fn posix_filename<R>(
45 self,
46 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
47 ) -> Result<R, Error> {
48 if crate::util::check_null_bytes(self.as_bytes())? {
49 function(self.as_ptr().cast())
50 } else {
51 let buffer = crate::util::copy_and_push(self.as_bytes(), 0);
52 function(buffer.as_ptr().cast())
53 }
54 }
55}
56
57impl AsFilename for &String {}
58impl Sealed for &String {
59 #[cfg(windows)]
60 fn windows_filename<R>(
61 self,
62 function: impl FnOnce(*const u16) -> Result<R, Error>,
63 ) -> Result<R, Error> {
64 self.as_str().windows_filename(function)
65 }
66
67 #[cfg(any(unix, target_os = "twizzler"))]
68 fn posix_filename<R>(
69 self,
70 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
71 ) -> Result<R, Error> {
72 self.as_str().posix_filename(function)
73 }
74}
75
76impl AsFilename for String {}
77impl Sealed for String {
78 #[cfg(windows)]
79 fn windows_filename<R>(
80 self,
81 function: impl FnOnce(*const u16) -> Result<R, Error>,
82 ) -> Result<R, Error> {
83 self.as_str().windows_filename(function)
84 }
85
86 #[cfg(any(unix, target_os = "twizzler"))]
87 fn posix_filename<R>(
88 mut self,
89 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
90 ) -> Result<R, Error> {
91 if crate::util::check_null_bytes(self.as_bytes())? {
92 function(self.as_ptr().cast())
93 } else {
94 self.push('\0');
95 function(self.as_ptr().cast())
96 }
97 }
98}
99
100#[cfg(feature = "std")]
101#[cfg_attr(libloading_docs, doc(cfg(feature = "std")))]
102mod std {
103 use std::ffi::{OsStr, OsString};
104
105 use super::{AsFilename, Sealed};
106 use crate::Error;
107
108 impl AsFilename for &OsStr {}
109 impl Sealed for &OsStr {
110 #[cfg(windows)]
111 fn windows_filename<R>(
112 self,
113 function: impl FnOnce(*const u16) -> Result<R, Error>,
114 ) -> Result<R, Error> {
115 use std::os::windows::ffi::OsStrExt;
116 let bytes = self.as_encoded_bytes();
117 let utf16: alloc::vec::Vec<u16> = if crate::util::check_null_bytes(bytes)? {
118 self.encode_wide().collect()
119 } else {
120 self.encode_wide().chain(Some(0)).collect()
121 };
122 function(utf16.as_ptr())
123 }
124
125 #[cfg(any(unix, target_os = "twizzler"))]
126 fn posix_filename<R>(
127 self,
128 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
129 ) -> Result<R, Error> {
130 #[cfg(unix)]
131 let bytes = std::os::unix::ffi::OsStrExt::as_bytes(self);
132 #[cfg(target_os = "twizzler")]
133 let bytes = std::os::twizzler::ffi::OsStrExt::as_bytes(self);
134 if crate::util::check_null_bytes(bytes)? {
135 function(bytes.as_ptr().cast())
136 } else {
137 let buffer = crate::util::copy_and_push(bytes, 0);
138 function(buffer.as_ptr().cast())
139 }
140 }
141 }
142
143 impl AsFilename for &OsString {}
144 impl Sealed for &OsString {
145 #[cfg(windows)]
146 fn windows_filename<R>(
147 self,
148 function: impl FnOnce(*const u16) -> Result<R, Error>,
149 ) -> Result<R, Error> {
150 self.as_os_str().windows_filename(function)
151 }
152
153 #[cfg(any(unix, target_os = "twizzler"))]
154 fn posix_filename<R>(
155 self,
156 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
157 ) -> Result<R, Error> {
158 self.as_os_str().posix_filename(function)
159 }
160 }
161
162 impl AsFilename for OsString {}
163 impl Sealed for OsString {
164 #[cfg(windows)]
165 fn windows_filename<R>(
166 self,
167 function: impl FnOnce(*const u16) -> Result<R, Error>,
168 ) -> Result<R, Error> {
169 self.as_os_str().windows_filename(function)
174 }
175
176 #[cfg(any(unix, target_os = "twizzler"))]
177 fn posix_filename<R>(
178 self,
179 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
180 ) -> Result<R, Error> {
181 #[cfg(unix)]
182 let mut bytes = std::os::unix::ffi::OsStringExt::into_vec(self);
183 #[cfg(target_os = "twizzler")]
184 let mut bytes = std::os::twizzler::ffi::OsStringExt::into_vec(self);
185 if crate::util::check_null_bytes(&bytes)? {
186 function(bytes.as_ptr().cast())
187 } else {
188 bytes.push(0);
189 function(bytes.as_ptr().cast())
190 }
191 }
192 }
193
194 impl AsFilename for std::path::PathBuf {}
195 impl Sealed for std::path::PathBuf {
196 #[cfg(windows)]
197 fn windows_filename<R>(
198 self,
199 function: impl FnOnce(*const u16) -> Result<R, Error>,
200 ) -> Result<R, Error> {
201 self.into_os_string().windows_filename(function)
202 }
203
204 #[cfg(any(unix, target_os = "twizzler"))]
205 fn posix_filename<R>(
206 self,
207 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
208 ) -> Result<R, Error> {
209 self.into_os_string().posix_filename(function)
210 }
211 }
212
213 impl AsFilename for &std::path::PathBuf {}
214 impl Sealed for &std::path::PathBuf {
215 #[cfg(windows)]
216 fn windows_filename<R>(
217 self,
218 function: impl FnOnce(*const u16) -> Result<R, Error>,
219 ) -> Result<R, Error> {
220 self.as_os_str().windows_filename(function)
221 }
222
223 #[cfg(any(unix, target_os = "twizzler"))]
224 fn posix_filename<R>(
225 self,
226 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
227 ) -> Result<R, Error> {
228 self.as_os_str().posix_filename(function)
229 }
230 }
231
232 impl AsFilename for &std::path::Path {}
233 impl Sealed for &std::path::Path {
234 #[cfg(windows)]
235 fn windows_filename<R>(
236 self,
237 function: impl FnOnce(*const u16) -> Result<R, Error>,
238 ) -> Result<R, Error> {
239 self.as_os_str().windows_filename(function)
240 }
241
242 #[cfg(any(unix, target_os = "twizzler"))]
243 fn posix_filename<R>(
244 self,
245 function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
246 ) -> Result<R, Error> {
247 self.as_os_str().posix_filename(function)
248 }
249 }
250}