Skip to content

Commit 8eeeef2

Browse files
rework to use traits instead of file names.
1 parent 1e50471 commit 8eeeef2

File tree

12 files changed

+794
-616
lines changed

12 files changed

+794
-616
lines changed

.github/workflows/libloading.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
strategy:
1616
fail-fast: false
1717
matrix:
18-
rust_toolchain: [nightly, stable, 1.71.0]
18+
rust_toolchain: [nightly, stable, 1.81.0]
1919
os: [ubuntu-latest, windows-latest, macOS-latest]
2020
timeout-minutes: 20
2121
steps:
@@ -24,10 +24,12 @@ jobs:
2424
- run: rustup default ${{ matrix.rust_toolchain }}
2525
- run: rustup component add clippy
2626
- run: cargo update -p libc --precise 0.2.155
27-
if: ${{ matrix.rust_toolchain == '1.71.0' }}
27+
if: ${{ matrix.rust_toolchain == '1.81.0' }}
2828
- run: cargo clippy
2929
- run: cargo test -- --nocapture
3030
- run: cargo test --release -- --nocapture
31+
- run: cargo test --no-default-features -- --nocapture
32+
- run: cargo test --release --no-default-features -- --nocapture
3133
- run: cargo rustdoc -Zunstable-options --config 'build.rustdocflags=["--cfg", "libloading_docs", "-D", "rustdoc::broken_intra_doc_links"]'
3234
if: ${{ matrix.rust_toolchain == 'nightly' }}
3335
# pwsh.exe drops quotes kekw. https://stackoverflow.com/a/59036879

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ readme = "README.mkd"
1212
description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety."
1313
keywords = ["dlopen", "load", "shared", "dylib"]
1414
categories = ["api-bindings"]
15-
rust-version = "1.71.0"
15+
rust-version = "1.81.0"
1616
edition = "2015"
1717

1818
[features]

src/as_filename.rs

Lines changed: 356 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,356 @@
1+
use alloc::ffi::CString;
2+
use alloc::string::String;
3+
use alloc::vec::Vec;
4+
use core::ffi::{c_char, CStr};
5+
use util::cstr_cow_from_bytes;
6+
use Error;
7+
8+
mod private {
9+
10+
pub trait AsFilenameSeal {
11+
#[allow(unused)] //Posix doesnt use this
12+
fn windows_filename<R>(
13+
&self,
14+
function: impl FnOnce(*const u16) -> Result<R, crate::Error>,
15+
) -> Result<R, crate::Error>;
16+
17+
#[allow(unused)] //Windows doesnt use this
18+
fn posix_filename<R>(
19+
&self,
20+
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, crate::Error>,
21+
) -> Result<R, crate::Error>;
22+
}
23+
}
24+
25+
/// This trait is implemented on all types where libloading can derrive a filename from.
26+
/// It is sealed and cannot be implemented by a user of libloading.
27+
///
28+
/// This trait is implemented for the following common types:
29+
/// - String &String &str
30+
/// - CString &CString &CStr
31+
/// - OsString &OsString &OsStr
32+
/// - PathBuf &PathBuf &Path
33+
/// - &[u8] assumes utf8 data!
34+
/// - &[u16] assumes utf16-ne data!
35+
///
36+
pub trait AsFilename: private::AsFilenameSeal {}
37+
38+
impl<T> AsFilename for T where T: private::AsFilenameSeal {}
39+
40+
impl private::AsFilenameSeal for &str {
41+
fn windows_filename<R>(
42+
&self,
43+
function: impl FnOnce(*const u16) -> Result<R, Error>,
44+
) -> Result<R, Error> {
45+
let mut utf16: Vec<u16> = self.encode_utf16().collect();
46+
if utf16.last() != Some(&0) {
47+
utf16.push(0);
48+
};
49+
function(utf16.as_ptr())
50+
}
51+
52+
fn posix_filename<R>(
53+
&self,
54+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
55+
) -> Result<R, Error> {
56+
let cow = cstr_cow_from_bytes(self.as_bytes())?;
57+
function(cow.as_ptr())
58+
}
59+
}
60+
61+
impl private::AsFilenameSeal for &String {
62+
fn windows_filename<R>(
63+
&self,
64+
function: impl FnOnce(*const u16) -> Result<R, Error>,
65+
) -> Result<R, Error> {
66+
self.as_str().windows_filename(function)
67+
}
68+
69+
fn posix_filename<R>(
70+
&self,
71+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
72+
) -> Result<R, Error> {
73+
let cow = cstr_cow_from_bytes(self.as_bytes())?;
74+
function(cow.as_ptr())
75+
}
76+
}
77+
78+
impl private::AsFilenameSeal for String {
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+
fn posix_filename<R>(
87+
&self,
88+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
89+
) -> Result<R, Error> {
90+
let cow = cstr_cow_from_bytes(self.as_bytes())?;
91+
function(cow.as_ptr())
92+
}
93+
}
94+
95+
impl private::AsFilenameSeal for &CStr {
96+
fn windows_filename<R>(
97+
&self,
98+
function: impl FnOnce(*const u16) -> Result<R, Error>,
99+
) -> Result<R, Error> {
100+
//We assume cstr is utf-8 here, if it's something bespoke like CESU-8 (thanks java) then yeah... no.
101+
let utf8 = self.to_str()?;
102+
utf8.windows_filename(function)
103+
}
104+
105+
fn posix_filename<R>(
106+
&self,
107+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
108+
) -> Result<R, Error> {
109+
function(self.as_ptr())
110+
}
111+
}
112+
113+
impl private::AsFilenameSeal for &CString {
114+
fn windows_filename<R>(
115+
&self,
116+
function: impl FnOnce(*const u16) -> Result<R, Error>,
117+
) -> Result<R, Error> {
118+
self.as_c_str().windows_filename(function)
119+
}
120+
121+
fn posix_filename<R>(
122+
&self,
123+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
124+
) -> Result<R, Error> {
125+
self.as_c_str().posix_filename(function)
126+
}
127+
}
128+
129+
impl private::AsFilenameSeal for CString {
130+
fn windows_filename<R>(
131+
&self,
132+
function: impl FnOnce(*const u16) -> Result<R, Error>,
133+
) -> Result<R, Error> {
134+
self.as_c_str().windows_filename(function)
135+
}
136+
137+
fn posix_filename<R>(
138+
&self,
139+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
140+
) -> Result<R, Error> {
141+
self.as_c_str().posix_filename(function)
142+
}
143+
}
144+
145+
/// This implementation assumes that a slice always contains utf-8 bytes.
146+
/// (which is likely the most common case if the slice originated in rust)
147+
impl private::AsFilenameSeal for &[u8] {
148+
fn windows_filename<R>(
149+
&self,
150+
function: impl FnOnce(*const u16) -> Result<R, Error>,
151+
) -> Result<R, Error> {
152+
let utf8 = core::str::from_utf8(self)?;
153+
utf8.windows_filename(function)
154+
}
155+
156+
fn posix_filename<R>(
157+
&self,
158+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
159+
) -> Result<R, Error> {
160+
let utf8 = core::str::from_utf8(self)?;
161+
utf8.posix_filename(function)
162+
}
163+
}
164+
165+
impl<const N: usize> private::AsFilenameSeal for [u8; N] {
166+
fn windows_filename<R>(
167+
&self,
168+
function: impl FnOnce(*const u16) -> Result<R, Error>,
169+
) -> Result<R, Error> {
170+
self.as_slice().windows_filename(function)
171+
}
172+
173+
fn posix_filename<R>(
174+
&self,
175+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
176+
) -> Result<R, Error> {
177+
self.as_slice().posix_filename(function)
178+
}
179+
}
180+
181+
impl<const N: usize> private::AsFilenameSeal for &[u8; N] {
182+
fn windows_filename<R>(
183+
&self,
184+
function: impl FnOnce(*const u16) -> Result<R, Error>,
185+
) -> Result<R, Error> {
186+
self.as_slice().windows_filename(function)
187+
}
188+
189+
fn posix_filename<R>(
190+
&self,
191+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
192+
) -> Result<R, Error> {
193+
self.as_slice().posix_filename(function)
194+
}
195+
}
196+
197+
/// This implementation assumes that the slice contains utf-16 in native endian.
198+
/// Sidenote: For windows this is always utf-16-le because the last big endian Windows system was the xbox 360 that rust doesn't support.
199+
/// For linux this is highly likely to also be utf-16-le because big endian is only used in some old mips routers or some IBM hardware.
200+
impl private::AsFilenameSeal for &[u16] {
201+
fn windows_filename<R>(
202+
&self,
203+
function: impl FnOnce(*const u16) -> Result<R, Error>,
204+
) -> Result<R, Error> {
205+
//Check that we have valid utf-16
206+
for c in core::char::decode_utf16(self.iter().copied()) {
207+
let _ = c?;
208+
}
209+
210+
if self.last() != Some(&0) {
211+
let mut copy = self.to_vec();
212+
copy.push(0);
213+
return function(copy.as_ptr());
214+
}
215+
216+
function(self.as_ptr())
217+
}
218+
219+
fn posix_filename<R>(
220+
&self,
221+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
222+
) -> Result<R, Error> {
223+
let utf8 = String::from_utf16(self)?;
224+
utf8.posix_filename(function)
225+
}
226+
}
227+
228+
#[cfg(feature = "std")]
229+
#[cfg(any(windows, unix))]
230+
mod std {
231+
use as_filename::private;
232+
use core::ffi::c_char;
233+
use std::ffi::{OsStr, OsString};
234+
use Error;
235+
236+
impl private::AsFilenameSeal for &OsStr {
237+
#[cfg(unix)]
238+
fn windows_filename<R>(
239+
&self,
240+
_function: impl FnOnce(*const u16) -> Result<R, Error>,
241+
) -> Result<R, Error> {
242+
panic!("windows_filename() not implemented for OsStr on posix platform");
243+
}
244+
245+
#[cfg(windows)]
246+
fn windows_filename<R>(
247+
&self,
248+
function: impl FnOnce(*const u16) -> Result<R, Error>,
249+
) -> Result<R, Error> {
250+
let mut utf16: alloc::vec::Vec<u16> =
251+
std::os::windows::ffi::OsStrExt::encode_wide(*self).collect();
252+
if utf16.last() != Some(&0) {
253+
utf16.push(0);
254+
};
255+
function(utf16.as_ptr())
256+
}
257+
258+
#[cfg(unix)]
259+
fn posix_filename<R>(
260+
&self,
261+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
262+
) -> Result<R, Error> {
263+
let cow =
264+
crate::util::cstr_cow_from_bytes(std::os::unix::ffi::OsStrExt::as_bytes(*self))?;
265+
function(cow.as_ptr())
266+
}
267+
268+
#[cfg(windows)]
269+
fn posix_filename<R>(
270+
&self,
271+
_function: impl FnOnce(*const c_char) -> Result<R, Error>,
272+
) -> Result<R, Error> {
273+
panic!("posix_filename() not implemented for OsStr on windows")
274+
}
275+
}
276+
277+
impl private::AsFilenameSeal for &OsString {
278+
fn windows_filename<R>(
279+
&self,
280+
function: impl FnOnce(*const u16) -> Result<R, Error>,
281+
) -> Result<R, Error> {
282+
self.as_os_str().windows_filename(function)
283+
}
284+
285+
fn posix_filename<R>(
286+
&self,
287+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
288+
) -> Result<R, Error> {
289+
self.as_os_str().posix_filename(function)
290+
}
291+
}
292+
293+
impl private::AsFilenameSeal for OsString {
294+
fn windows_filename<R>(
295+
&self,
296+
function: impl FnOnce(*const u16) -> Result<R, Error>,
297+
) -> Result<R, Error> {
298+
self.as_os_str().windows_filename(function)
299+
}
300+
301+
fn posix_filename<R>(
302+
&self,
303+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
304+
) -> Result<R, Error> {
305+
self.as_os_str().posix_filename(function)
306+
}
307+
}
308+
309+
impl private::AsFilenameSeal for std::path::PathBuf {
310+
fn windows_filename<R>(
311+
&self,
312+
function: impl FnOnce(*const u16) -> Result<R, Error>,
313+
) -> Result<R, Error> {
314+
self.as_os_str().windows_filename(function)
315+
}
316+
317+
fn posix_filename<R>(
318+
&self,
319+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
320+
) -> Result<R, Error> {
321+
self.as_os_str().posix_filename(function)
322+
}
323+
}
324+
325+
impl private::AsFilenameSeal for &std::path::PathBuf {
326+
fn windows_filename<R>(
327+
&self,
328+
function: impl FnOnce(*const u16) -> Result<R, Error>,
329+
) -> Result<R, Error> {
330+
self.as_os_str().windows_filename(function)
331+
}
332+
333+
fn posix_filename<R>(
334+
&self,
335+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
336+
) -> Result<R, Error> {
337+
self.as_os_str().posix_filename(function)
338+
}
339+
}
340+
341+
impl private::AsFilenameSeal for &std::path::Path {
342+
fn windows_filename<R>(
343+
&self,
344+
function: impl FnOnce(*const u16) -> Result<R, Error>,
345+
) -> Result<R, Error> {
346+
self.as_os_str().windows_filename(function)
347+
}
348+
349+
fn posix_filename<R>(
350+
&self,
351+
function: impl FnOnce(*const c_char) -> Result<R, Error>,
352+
) -> Result<R, Error> {
353+
self.as_os_str().posix_filename(function)
354+
}
355+
}
356+
}

0 commit comments

Comments
 (0)