Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/libloading.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
rust_toolchain: [nightly, stable, 1.71.0]
rust_toolchain: [nightly, stable, 1.88.0]
os: [ubuntu-latest, windows-latest, macOS-latest]
timeout-minutes: 20
steps:
Expand All @@ -24,15 +24,30 @@ jobs:
- run: rustup default ${{ matrix.rust_toolchain }}
- run: rustup component add clippy
- run: cargo update -p libc --precise 0.2.155
if: ${{ matrix.rust_toolchain == '1.71.0' }}
if: ${{ matrix.rust_toolchain == '1.88.0' }}
- run: cargo clippy
- run: cargo test -- --nocapture
- run: cargo test --release -- --nocapture
- run: cargo test --no-default-features -- --nocapture
- run: cargo test --release --no-default-features -- --nocapture
- run: cargo rustdoc -Zunstable-options --config 'build.rustdocflags=["--cfg", "libloading_docs", "-D", "rustdoc::broken_intra_doc_links"]'
if: ${{ matrix.rust_toolchain == 'nightly' }}
# pwsh.exe drops quotes kekw. https://stackoverflow.com/a/59036879
shell: bash

no-std-build-test:
runs-on: ubuntu-latest
# This will rebuild core and alloc, which will re-mangle symbols in alloc,
# which then will break the pre-compiled std. This will cause the program to fail to link.
# This test does not test anything in an actual no-std environment only that the library compiles on it.
# We test all no-std only functions together with the ordinary functions.
steps:
- uses: actions/checkout@v4
- name: Add nightly toolchain
run: rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
- name: Compile without the standard library
run: cargo clean && cargo +nightly build -Zbuild-std=core,alloc --no-default-features

windows-test:
runs-on: windows-latest
strategy:
Expand Down
10 changes: 7 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "libloading"
# When bumping
# * Don’t forget to add an entry to `src/changelog.rs`
# * If bumping to an incompatible version, adjust the documentation in `src/lib.rs`
version = "0.8.9"
version = "0.9.0"
authors = ["Simonas Kazlauskas <libloading@kazlauskas.me>"]
license = "ISC"
repository = "https://github.com/nagisa/rust_libloading/"
Expand All @@ -12,8 +12,12 @@ readme = "README.mkd"
description = "Bindings around the platform's dynamic library loading primitives with greatly improved memory safety."
keywords = ["dlopen", "load", "shared", "dylib"]
categories = ["api-bindings"]
rust-version = "1.71.0"
edition = "2015"
rust-version = "1.88.0"
edition = "2021"

[features]
default = ["std"]
std = []

[target.'cfg(windows)'.dependencies.windows-link]
version = "0.2"
Expand Down
242 changes: 242 additions & 0 deletions src/as_filename.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
use crate::Error;
use alloc::string::String;

pub(crate) trait Sealed {
#[cfg(windows)]
#[doc(hidden)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, crate::Error>,
) -> Result<R, crate::Error>;

#[cfg(unix)]
#[doc(hidden)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, crate::Error>,
) -> Result<R, crate::Error>;
}

/// This trait is implemented for types that can be used as a filename when loading new
/// [`Library`](crate::Library) instances.
///
/// It is currently sealed and cannot be implemented or its methods called by users of this crate.
#[expect(private_bounds)]
pub trait AsFilename: Sealed {}

impl AsFilename for &str {}
impl Sealed for &str {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
let utf16: alloc::vec::Vec<u16> = if crate::util::check_null_bytes(self.as_bytes())? {
self.encode_utf16().collect()
} else {
self.encode_utf16().chain(Some(0)).collect()
};
function(utf16.as_ptr())
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
if crate::util::check_null_bytes(self.as_bytes())? {
function(self.as_ptr().cast())
} else {
let buffer = crate::util::copy_and_push(self.as_bytes(), 0);
function(buffer.as_ptr().cast())
}
}
}

impl AsFilename for &String {}
impl Sealed for &String {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_str().posix_filename(function)
}
}

impl AsFilename for String {}
impl Sealed for String {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
mut self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
if crate::util::check_null_bytes(self.as_bytes())? {
function(self.as_ptr().cast())
} else {
self.push('\0');
function(self.as_ptr().cast())
}
}
}

#[cfg(feature = "std")]
#[cfg_attr(libloading_docs, doc(cfg(feature = "std")))]
mod std {
use super::{Sealed, AsFilename};
use crate::Error;
use std::ffi::{OsStr, OsString};

impl AsFilename for &OsStr {}
impl Sealed for &OsStr {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
use std::os::windows::ffi::OsStrExt;
let bytes = self.as_encoded_bytes();
let utf16: alloc::vec::Vec<u16> = if crate::util::check_null_bytes(bytes)? {
self.encode_wide().collect()
} else {
self.encode_wide().chain(Some(0)).collect()
};
function(utf16.as_ptr())
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
let bytes = std::os::unix::ffi::OsStrExt::as_bytes(self);
if crate::util::check_null_bytes(bytes)? {
function(bytes.as_ptr().cast())
} else {
let buffer = crate::util::copy_and_push(bytes, 0);
function(buffer.as_ptr().cast())
}
}
}

impl AsFilename for &OsString {}
impl Sealed for &OsString {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().posix_filename(function)
}
}

impl AsFilename for OsString {}
impl Sealed for OsString {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
// This is the best we can do.
// There is no into_wide for windows.
// The internal repr is wtf-8 and this is different
// from LCPWSTR that we need for the ffi calls.
self.as_os_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
let mut bytes = std::os::unix::ffi::OsStringExt::into_vec(self);
if crate::util::check_null_bytes(&bytes)? {
function(bytes.as_ptr().cast())
} else {
bytes.push(0);
function(bytes.as_ptr().cast())
}
}
}

impl AsFilename for std::path::PathBuf {}
impl Sealed for std::path::PathBuf {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.into_os_string().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
self.into_os_string().posix_filename(function)
}
}

impl AsFilename for &std::path::PathBuf {}
impl Sealed for &std::path::PathBuf {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().posix_filename(function)
}
}

impl AsFilename for &std::path::Path {}
impl Sealed for &std::path::Path {
#[cfg(windows)]
fn windows_filename<R>(
self,
function: impl FnOnce(*const u16) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().windows_filename(function)
}

#[cfg(unix)]
fn posix_filename<R>(
self,
function: impl FnOnce(*const core::ffi::c_char) -> Result<R, Error>,
) -> Result<R, Error> {
self.as_os_str().posix_filename(function)
}
}
}
Loading