Skip to main content

libloading/
as_filename.rs

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