libloading/
as_filename.rs

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/// This trait is implemented for types that can be used as a filename when loading new
22/// [`Library`](crate::Library) instances.
23///
24/// It is currently sealed and cannot be implemented or its methods called by users of this crate.
25#[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            // This is the best we can do.
170            // There is no into_wide for windows.
171            // The internal repr is wtf-8 and this is different
172            // from LCPWSTR that we need for the ffi calls.
173            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}