use crate::{
bindings::{nethost::get_hostfxr_parameters, MAX_PATH},
error::{HostingError, HostingResult, HostingSuccess},
hostfxr::Hostfxr,
pdcstring::{self, PdCStr, PdUChar},
};
use std::{ffi::OsString, mem::MaybeUninit, ptr};
use thiserror::Error;
pub fn get_hostfxr_path() -> Result<OsString, HostingError> {
unsafe { get_hostfxr_path_with_parameters(ptr::null()) }
}
pub fn get_hostfxr_path_with_assembly_path<P: AsRef<PdCStr>>(
assembly_path: P,
) -> Result<OsString, HostingError> {
let parameters = get_hostfxr_parameters::with_assembly_path(assembly_path.as_ref().as_ptr());
unsafe { get_hostfxr_path_with_parameters(¶meters) }
}
pub fn get_hostfxr_path_with_dotnet_root<P: AsRef<PdCStr>>(
dotnet_root: P,
) -> Result<OsString, HostingError> {
let parameters = get_hostfxr_parameters::with_dotnet_root(dotnet_root.as_ref().as_ptr());
unsafe { get_hostfxr_path_with_parameters(¶meters) }
}
unsafe fn get_hostfxr_path_with_parameters(
parameters: *const get_hostfxr_parameters,
) -> Result<OsString, HostingError> {
let mut path_buffer = maybe_uninit_uninit_array::<PdUChar, MAX_PATH>();
let mut path_length = path_buffer.len();
let result = unsafe {
crate::bindings::nethost::get_hostfxr_path(
path_buffer.as_mut_ptr().cast(),
&mut path_length,
parameters,
)
};
match HostingResult::from(result).into_result() {
Ok(_) => {
let path_slice =
unsafe { maybe_uninit_slice_assume_init_ref(&path_buffer[..path_length]) };
Ok(unsafe { PdCStr::from_slice_with_nul_unchecked(path_slice) }.to_os_string())
}
Err(HostingError::HostApiBufferTooSmall) => {
let mut path_vec = Vec::new();
path_vec.resize(path_length, MaybeUninit::<pdcstring::PdUChar>::uninit());
let result = unsafe {
crate::bindings::nethost::get_hostfxr_path(
path_vec[0].as_mut_ptr().cast(),
&mut path_length,
parameters,
)
};
assert_eq!(result as u32, HostingSuccess::Success.value());
let path_slice =
unsafe { maybe_uninit_slice_assume_init_ref(&path_vec[..path_length]) };
Ok(unsafe { PdCStr::from_slice_with_nul_unchecked(path_slice) }.to_os_string())
}
Err(err) => Err(err),
}
}
pub fn load_hostfxr() -> Result<Hostfxr, LoadHostfxrError> {
let hostfxr_path = get_hostfxr_path()?;
let hostfxr = Hostfxr::load_from_path(hostfxr_path)?;
Ok(hostfxr)
}
pub fn load_hostfxr_with_assembly_path<P: AsRef<PdCStr>>(
assembly_path: P,
) -> Result<Hostfxr, LoadHostfxrError> {
let hostfxr_path = get_hostfxr_path_with_assembly_path(assembly_path)?;
let hostfxr = Hostfxr::load_from_path(hostfxr_path)?;
Ok(hostfxr)
}
pub fn load_hostfxr_with_dotnet_root<P: AsRef<PdCStr>>(
dotnet_root: P,
) -> Result<Hostfxr, LoadHostfxrError> {
let hostfxr_path = get_hostfxr_path_with_dotnet_root(dotnet_root)?;
let hostfxr = Hostfxr::load_from_path(hostfxr_path)?;
Ok(hostfxr)
}
#[derive(Debug, Error)]
pub enum LoadHostfxrError {
#[error(transparent)]
Hosting(#[from] HostingError),
#[error(transparent)]
DlOpen(#[from] crate::dlopen2::Error),
}
const unsafe fn maybe_uninit_slice_assume_init_ref<T>(slice: &[MaybeUninit<T>]) -> &[T] {
#[cfg(any(nightly, feature = "nightly"))]
unsafe {
MaybeUninit::slice_assume_init_ref(slice)
}
#[cfg(not(any(nightly, feature = "nightly")))]
unsafe {
&*(slice as *const [MaybeUninit<T>] as *const [T])
}
}
fn maybe_uninit_uninit_array<T, const LEN: usize>() -> [MaybeUninit<T>; LEN] {
#[cfg(any(nightly, feature = "nightly"))]
return MaybeUninit::<T>::uninit_array::<LEN>();
#[cfg(not(any(nightly, feature = "nightly")))]
unsafe {
MaybeUninit::<[MaybeUninit<T>; LEN]>::uninit().assume_init()
}
}