host_discovery/
lib.rs

1#[warn(missing_docs, missing_debug_implementations)]
2#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
3use raw_cpuid::CpuId;
4#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
5use raw_cpuid::ProcessorBrandString;
6#[cfg(target_os = "linux")]
7use rayon::prelude::*;
8#[cfg(target_os = "linux")]
9use std::fs;
10#[cfg(target_os = "linux")]
11use std::path::Path;
12#[cfg(target_os = "macos")]
13use std::process::Command;
14use std::env::consts::{ARCH, OS};
15use wgpu::{Backends, Instance};
16#[cfg(target_os = "windows")]
17use windows_registry::LOCAL_MACHINE;
18
19mod constants;
20#[cfg(target_os = "linux")]
21use constants::WSL_INTEROP_PATH;
22#[cfg(target_os = "windows")]
23use constants::{COMPUTER_NAME_PATH, WIN_EDITION_PATH};
24mod display;
25#[cfg(test)]
26mod tests;
27
28#[derive(Debug)]
29pub struct OSProfile<'o, 'a> {
30    pub os: &'o str,
31    pub arch: &'a str,
32    pub win_edition: Option<String>,
33    pub computer_name: Option<String>,
34    pub is_wsl: Option<bool>,
35    pub distro: Option<String>,
36    pub hostname: Option<String>,
37}
38
39#[derive(Debug)]
40pub struct Processor<M, C> {
41    pub model: M,
42    pub cores: C,
43}
44
45#[derive(Debug)]
46pub struct GraphicsCard {
47    pub model: String,
48    pub driver_version: String,
49}
50
51// shorthand macros for implementing the Display trait
52display_profile!(OSProfile);
53display_graphics!(GraphicsCard);
54display_processor!(Processor<M, C>);
55
56impl<'o, 'a> OSProfile<'o, 'a> {
57    pub fn new() -> Self {
58        Self {
59            os: OS,
60            arch: ARCH,
61            win_edition: None,
62            computer_name: None,
63            is_wsl: None,
64            distro: None,
65            hostname: None,
66        }
67    }
68
69    /// Returns the Windows Edition if a Windows system is available
70    #[cfg(target_os = "windows")]
71    pub fn win_edition(mut self) -> Self {
72        let key = LOCAL_MACHINE;
73        let sub_key = key
74            .open(WIN_EDITION_PATH)
75            .expect("Failed to find registry entry for: CurrentVersion");
76        let edition = sub_key
77            .get_string("EditionID")
78            .expect("Failed to identify Windows Edition");
79
80        self.win_edition = Some(edition);
81        self
82    }
83
84    /// Returns the ComputerName if a Windows system is available
85    #[cfg(target_os = "windows")]
86    pub fn computer_name(mut self) -> Self {
87        let key = LOCAL_MACHINE;
88        let sub_key = key
89            .open(COMPUTER_NAME_PATH)
90            .expect("Failed to find registry entry for: ComputerName");
91        let name = sub_key
92            .get_string("ComputerName")
93            .expect("Failed to find key: ComputerName");
94
95        self.computer_name = Some(name);
96        self
97    }
98
99    /// Returns the Linux distro if a Linux system is available
100    #[cfg(target_os = "linux")]
101    pub fn distro(mut self) -> Self {
102        let text = fs::read_to_string("/etc/os-release").expect("Failed to read /etc/os-release");
103        let tokens = text.split("\n").collect::<Vec<&str>>();
104        let pretty_name = tokens
105            .par_iter()
106            .filter(|line| line.contains("PRETTY_NAME"))
107            .collect::<Vec<&&str>>();
108
109        let distro = pretty_name[0].split("=").collect::<Vec<&str>>()[1].replace("\"", "");
110        self.distro = Some(distro);
111        self
112    }
113
114    /// Returns the hostname if a Linux system is available
115    #[cfg(target_os = "linux")]
116    pub fn hostname(mut self) -> Self {
117        let name = fs::read_to_string("/etc/hostname").expect("Failed to read /etc/hostname");
118        self.hostname = Some(name.split("\n").collect::<String>());
119        self
120    }
121
122    /// Returns true if the Linux host is running on WSL
123    #[cfg(target_os = "linux")]
124    pub fn is_wsl(mut self) -> Self {
125        let path = Path::new(WSL_INTEROP_PATH).exists();
126        self.is_wsl = Some(path);
127        self
128    }
129
130    pub fn build(self) -> Self {
131        Self {
132            os: self.os,
133            arch: self.arch,
134            win_edition: self.win_edition,
135            computer_name: self.computer_name,
136            is_wsl: self.is_wsl,
137            distro: self.distro,
138            hostname: self.hostname,
139        }
140    }
141}
142
143/// Returns a `Processor` object containing the CPU model and logical core count (macOS only)
144#[cfg(target_os = "macos")]
145pub fn sysctl_cpu() -> Processor<String, String> {
146    let get_sysctl_output = |arg: &str| -> String {
147        let output = Command::new("sysctl")
148            .arg(arg)
149            .output()
150            .expect("Failed to execute sysctl command");
151        String::from_utf8(output.stdout)
152            .unwrap()
153            .split(": ")
154            .nth(1)
155            .unwrap()
156            .trim()
157            .to_string()
158    };
159
160    Processor {
161        model: get_sysctl_output("machdep.cpu.brand_string"),
162        cores: get_sysctl_output("hw.logicalcpu"),
163    }
164}
165
166/// Returns a `Processor` object containing the CPU model and logical core count  (x86 only)
167#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
168pub fn x86_cpu() -> Processor<ProcessorBrandString, u32> {
169    let cpuid = CpuId::new();
170    let brand = cpuid.get_processor_brand_string().unwrap();
171    let cores = cpuid.get_processor_capacity_feature_info().unwrap();
172
173    Processor {
174        model: brand,
175        cores: cores.maximum_logical_processors() as u32,
176    }
177}
178
179/// Returns a `GraphicsCard` object containing the GPU model and driver version
180pub fn gpu() -> Option<GraphicsCard> {
181    let instance = Instance::default();
182    for adapter in instance.enumerate_adapters(Backends::all()) {
183        let info = adapter.get_info();
184        let gpu = GraphicsCard {
185                model: info.name,
186                driver_version: info.driver_info,
187        };
188        return Some(gpu);
189    }
190    None
191}