Skip to main content

netcorehost/hostfxr/
library.rs

1use crate::{
2    dlopen2::wrapper::Container,
3    error::{HostingError, HostingResult},
4    pdcstring::PdCString,
5};
6use derive_more::From;
7use std::{
8    env::consts::EXE_SUFFIX,
9    ffi::OsString,
10    path::{Path, PathBuf},
11    sync::Arc,
12};
13
14pub(crate) type HostfxrLibrary = Container<crate::bindings::hostfxr::wrapper_option::Hostfxr>;
15pub(crate) type SharedHostfxrLibrary = Arc<HostfxrLibrary>;
16#[allow(unused, clippy::cast_possible_wrap)]
17pub(crate) const UNSUPPORTED_HOST_VERSION_ERROR_CODE: i32 =
18    HostingError::HostApiUnsupportedVersion.value() as i32;
19
20/// A struct representing a loaded hostfxr library.
21#[derive(Clone, From)]
22pub struct Hostfxr {
23    /// The underlying hostfxr library.
24    pub lib: SharedHostfxrLibrary,
25    pub(crate) dotnet_exe: PdCString,
26}
27
28fn find_dotnet_bin(hostfxr_path: impl AsRef<Path>) -> PathBuf {
29    let mut p = hostfxr_path.as_ref().to_path_buf();
30    loop {
31        if let Some(dir) = p.file_name() {
32            if dir == "dotnet" || dir == ".dotnet" {
33                break;
34            }
35            p.pop();
36        } else {
37            p.clear();
38            break;
39        }
40    }
41    p.push("dotnet");
42    let mut p = OsString::from(p);
43    p.extend(Path::new(EXE_SUFFIX));
44    PathBuf::from(p)
45}
46
47impl Hostfxr {
48    /// Loads the hostfxr library from the given path.
49    pub fn load_from_path(path: impl AsRef<Path>) -> Result<Self, crate::dlopen2::Error> {
50        let path = path.as_ref();
51        let lib = SharedHostfxrLibrary::new(unsafe { Container::load(path) }?);
52
53        // Some APIs of hostfxr.dll require a path to the dotnet executable, so we try to locate it here based on the hostfxr path.
54        let dotnet_exe = PdCString::from_os_str(find_dotnet_bin(path)).unwrap();
55
56        Ok(Self { lib, dotnet_exe })
57    }
58
59    /// Locates the hostfxr library using [`nethost`](crate::nethost) and loads it.
60    #[cfg(feature = "nethost")]
61    pub fn load_with_nethost() -> Result<Self, crate::nethost::LoadHostfxrError> {
62        crate::nethost::load_hostfxr()
63    }
64
65    /// Returns the path to the dotnet root.
66    #[must_use]
67    pub fn get_dotnet_root(&self) -> PathBuf {
68        self.get_dotnet_exe().parent().unwrap().to_owned()
69    }
70
71    /// Returns the path to the dotnet executable of the same installation as hostfxr.
72    #[must_use]
73    pub fn get_dotnet_exe(&self) -> PathBuf {
74        self.dotnet_exe.to_os_string().into()
75    }
76}
77
78/// Either the exit code of the app if it ran successful, otherwise the error from the hosting components.
79#[repr(transparent)]
80#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81pub struct AppOrHostingResult(i32);
82
83impl AppOrHostingResult {
84    /// Gets the raw value of the result.
85    #[must_use]
86    pub const fn value(&self) -> i32 {
87        self.0
88    }
89
90    /// Converts the result to an hosting exit code.
91    pub fn as_hosting_exit_code(self) -> HostingResult {
92        HostingResult::from(self.0)
93    }
94}
95
96impl From<AppOrHostingResult> for i32 {
97    fn from(code: AppOrHostingResult) -> Self {
98        code.value()
99    }
100}
101
102impl From<i32> for AppOrHostingResult {
103    fn from(code: i32) -> Self {
104        Self(code)
105    }
106}