sys_info_extended/
lib.rs

1//! #Introduction
2//! This crate focuses on geting system information.
3//!
4//! For now it supports Linux, Mac OS X and Windows.
5//! And now it can get information of kernel/cpu/memory/disk/load/hostname and so on.
6//!
7
8extern crate libc;
9extern crate palin;
10
11use std::ffi;
12use std::fmt;
13use std::fmt::Display;
14use std::io::{self, Read};
15use std::fs::File;
16#[cfg(any(target_os = "windows", target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
17use std::os::raw::c_char;
18#[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "haiku")))]
19use std::os::raw::{c_int, c_double};
20
21#[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
22use libc::sysctl;
23#[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
24use std::mem::size_of_val;
25#[cfg(any(target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
26use std::ptr::null_mut;
27#[cfg(not(target_os = "windows"))]
28use libc::timeval;
29#[cfg(any(target_os = "solaris", target_os = "illumos"))]
30use std::time::SystemTime;
31#[cfg(target_os = "linux")]
32use std::collections::HashMap;
33
34#[cfg(any(target_os = "solaris", target_os = "illumos"))]
35mod kstat;
36
37#[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))]
38static OS_CTL_KERN: libc::c_int = 1;
39#[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))]
40static OS_KERN_BOOTTIME: libc::c_int = 21;
41
42/// System load average value.
43#[repr(C)]
44#[derive(Debug)]
45pub struct LoadAvg {
46    /// Average load within one minutes.
47    pub one: f64,
48    /// Average load within five minutes.
49    pub five: f64,
50    /// Average load within fifteen minutes.
51    pub fifteen: f64,
52}
53
54/// System memory information.
55#[repr(C)]
56#[derive(Debug)]
57pub struct MemInfo {
58    /// Total physical memory.
59    pub total: u64,
60    pub free: u64,
61    pub avail: u64,
62
63    pub buffers: u64,
64    pub cached: u64,
65
66    /// Total swap memory.
67    pub swap_total: u64,
68    pub swap_free: u64,
69}
70
71/// The os release info of Linux.
72///
73/// See [man os-release](https://www.freedesktop.org/software/systemd/man/os-release.html).
74#[derive(Debug)]
75#[derive(Default)]
76pub struct LinuxOSReleaseInfo {
77    /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-")
78    /// identifying the operating system, excluding any version information and suitable for
79    /// processing by scripts or usage in generated filenames.
80    ///
81    /// Note that we don't verify that the string is lower-case and can be used in file-names. If
82    /// the /etc/os-release file has an invalid value, you will get this value.
83    ///
84    /// If not set, defaults to "ID=linux". Use `self.id()` to fallback to the default.
85    ///
86    /// Example: "fedora" or "debian".
87    pub id: Option<String>,
88
89    /// A space-separated list of operating system identifiers in the same syntax as the ID=
90    /// setting. It should list identifiers of operating systems that are closely related to the
91    /// local operating system in regards to packaging and programming interfaces, for example
92    /// listing one or more OS identifiers the local OS is a derivative from. An OS should
93    /// generally only list other OS identifiers it itself is a derivative of, and not any OSes
94    /// that are derived from it, though symmetric relationships are possible. Build scripts and
95    /// similar should check this variable if they need to identify the local operating system and
96    /// the value of ID= is not recognized. Operating systems should be listed in order of how
97    /// closely the local operating system relates to the listed ones, starting with the closest.
98    ///
99    /// This field is optional.
100    ///
101    /// Example: for an operating system with `ID=centos`, an assignment of `ID_LIKE="rhel fedora"`
102    /// would be appropriate. For an operating system with `ID=ubuntu`, an assignment of
103    /// `ID_LIKE=debian` is appropriate.
104    pub id_like: Option<String>,
105
106    /// A string identifying the operating system, without a version component, and suitable for
107    /// presentation to the user.
108    ///
109    /// If not set, defaults to "NAME=Linux".Use `self.id()` to fallback to the default.
110    ///
111    /// Example: "Fedora" or "Debian GNU/Linux".
112    pub name: Option<String>,
113
114    /// A pretty operating system name in a format suitable for presentation to the user. May or
115    /// may not contain a release code name or OS version of some kind, as suitable.
116    ///
117    /// If not set, defaults to "Linux". Use `self.id()` to fallback to the default.
118    ///
119    /// Example: "Fedora 17 (Beefy Miracle)".
120    pub pretty_name: Option<String>,
121
122    /// A string identifying the operating system version, excluding any OS name information,
123    /// possibly including a release code name, and suitable for presentation to the user.
124    ///
125    /// This field is optional.
126    ///
127    /// Example: "17" or "17 (Beefy Miracle)"
128    pub version: Option<String>,
129
130    /// A lower-case string (mostly numeric, no spaces or other characters outside of 0–9, a–z,
131    /// ".", "_" and "-") identifying the operating system version, excluding any OS name
132    /// information or release code name, and suitable for processing by scripts or usage in
133    /// generated filenames.
134    ///
135    /// This field is optional.
136    ///
137    /// Example: "17" or "11.04".
138    pub version_id: Option<String>,
139
140    /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-")
141    /// identifying the operating system release code name, excluding any OS name information or
142    /// release version, and suitable for processing by scripts or usage in generated filenames.
143    ///
144    /// This field is optional and may not be implemented on all systems.
145    ///
146    /// Examples: "buster", "xenial".
147    pub version_codename: Option<String>,
148
149    /// A suggested presentation color when showing the OS name on the console. This should be
150    /// specified as string suitable for inclusion in the ESC [ m ANSI/ECMA-48 escape code for
151    /// setting graphical rendition.
152    ///
153    /// This field is optional.
154    ///
155    /// Example: "0;31" for red, "1;34" for light blue, or "0;38;2;60;110;180" for Fedora blue.
156    pub ansi_color: Option<String>,
157
158    /// A string, specifying the name of an icon as defined by freedesktop.org Icon Theme
159    /// Specification. This can be used by graphical applications to display an operating
160    /// system's or distributor's logo.
161    ///
162    /// This field is optional and may not necessarily be implemented on all systems.
163    ///
164    /// Examples: "LOGO=fedora-logo", "LOGO=distributor-logo-opensuse".
165    pub logo: Option<String>,
166
167    /// A CPE name for the operating system, in URI binding syntax, following the Common Platform
168    /// Enumeration Specification as proposed by the NIST.
169    ///
170    /// This field is optional.
171    ///
172    /// Example: "cpe:/o:fedoraproject:fedora:17".
173    pub cpe_name: Option<String>,
174
175    /// A string uniquely identifying the system image used as the origin for a distribution (it is
176    /// not updated with system updates). The field can be identical between different VERSION_IDs
177    /// as BUILD_ID is an only a unique identifier to a specific version. Distributions that
178    /// release each update as a new version would only need to use VERSION_ID as each build is
179    /// already distinct based on the VERSION_ID.
180    ///
181    /// This field is optional.
182    ///
183    /// Example: "2013-03-20.3" or "BUILD_ID=201303203".
184    pub build_id: Option<String>,
185
186    /// A string identifying a specific variant or edition of the operating system suitable for
187    /// presentation to the user. This field may be used to inform the user that the configuration
188    /// of this system is subject to a specific divergent set of rules or default configuration
189    /// settings.
190    ///
191    /// This field is optional and may not be implemented on all systems.
192    ///
193    /// Examples: "Server Edition", "Smart Refrigerator Edition".
194    ///
195    /// Note: this field is for display purposes only. The VARIANT_ID field should be used for
196    /// making programmatic decisions.
197    pub variant: Option<String>,
198
199    /// A lower-case string (no spaces or other characters outside of 0–9, a–z, ".", "_" and "-"),
200    /// identifying a specific variant or edition of the operating system. This may be interpreted
201    /// by other packages in order to determine a divergent default configuration.
202    ///
203    /// This field is optional and may not be implemented on all systems.
204    ///
205    /// Examples: "server", "embedded".
206    pub variant_id: Option<String>,
207
208    /// HOME_URL= should refer to the homepage of the operating system, or alternatively some homepage of
209    /// the specific version of the operating system.
210    ///
211    /// These URLs are intended to be exposed in "About this system" UIs behind links with captions
212    /// such as "About this Operating System", "Obtain Support", "Report a Bug", or "Privacy
213    /// Policy". The values should be in RFC3986 format, and should be "http:" or "https:" URLs,
214    /// and possibly "mailto:" or "tel:". Only one URL shall be listed in each setting. If multiple
215    /// resources need to be referenced, it is recommended to provide an online landing page
216    /// linking all available resources.
217    ///
218    /// Example: "https://fedoraproject.org/".
219    pub home_url: Option<String>,
220
221    /// DOCUMENTATION_URL= should refer to the main documentation page for this operating system.
222    ///
223    /// See also `home_url`.
224    pub documentation_url: Option<String>,
225
226    /// SUPPORT_URL= should refer to the main support page for the operating system, if there is
227    /// any. This is primarily intended for operating systems which vendors provide support for.
228    ///
229    /// See also `home_url`.
230    pub support_url: Option<String>,
231
232    /// BUG_REPORT_URL= should refer to the main bug reporting page for the operating system, if
233    /// there is any. This is primarily intended for operating systems that rely on community QA.
234    ///
235    /// Example: "https://bugzilla.redhat.com/".
236    ///
237    /// See also `home_url`.
238    pub bug_report_url: Option<String>,
239
240    /// PRIVACY_POLICY_URL= should refer to the main privacy policy page for the operating system,
241    /// if there is any. These settings are optional, and providing only some of these settings is
242    /// common.
243    ///
244    /// See also `home_url`.
245    pub privacy_policy_url: Option<String>,
246}
247
248macro_rules! os_release_defaults {
249    (
250        $(
251            $(#[$meta:meta])*
252            $vis:vis fn $field:ident => $default:literal
253        )*
254    ) => {
255        $(
256            $(#[$meta])*
257            $vis fn $field(&self) -> &str {
258                match self.$field.as_ref() {
259                    Some(value) => value,
260                    None => $default,
261                }
262            }
263        )*
264    }
265}
266
267impl LinuxOSReleaseInfo {
268    os_release_defaults!(
269        /// Returns the value of `self.id` or, if `None`, "linux" (the default value).
270        pub fn id => "linux"
271        /// Returns the value of `self.name` or, if `None`, "Linux" (the default value).
272        pub fn name => "Linux"
273        /// Returns the value of `self.pretty_name` or, if `None`, "Linux" (the default value).
274        pub fn pretty_name => "Linux"
275    );
276}
277
278/// Disk information.
279#[repr(C)]
280#[derive(Debug)]
281pub struct DiskInfo {
282    pub total: u64,
283    pub free: u64,
284}
285
286/// Error types
287#[derive(Debug)]
288pub enum Error {
289    UnsupportedSystem,
290    ExecFailed(io::Error),
291    IO(io::Error),
292    SystemTime(std::time::SystemTimeError),
293    General(String),
294    Other(String),
295    Unknown,
296}
297
298impl fmt::Display for Error {
299    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
300        use self::Error::*;
301        match *self {
302            UnsupportedSystem => write!(fmt, "System is not supported"),
303            ExecFailed(ref e) => write!(fmt, "Execution failed: {}", e),
304            IO(ref e) => write!(fmt, "IO error: {}", e),
305            SystemTime(ref e) => write!(fmt, "System time error: {}", e),
306            General(ref e) => write!(fmt, "Error: {}", e),
307            Unknown => write!(fmt, "An unknown error occurred"),
308            Other(ref e) => write!(fmt, "Error: {}", e)
309        }
310    }
311}
312
313impl std::error::Error for Error {
314    fn description(&self) -> &str {
315        use self::Error::*;
316        match *self {
317            UnsupportedSystem => "unsupported system",
318            ExecFailed(_) => "execution failed",
319            IO(_) => "io error",
320            SystemTime(_) => "system time",
321            General(_) => "general error",
322            Other(_) => "other error",
323            Unknown => "unknown error",
324        }
325    }
326
327    fn cause(&self) -> Option<&dyn std::error::Error> {
328        use self::Error::*;
329        match *self {
330            UnsupportedSystem => None,
331            ExecFailed(ref e) => Some(e),
332            IO(ref e) => Some(e),
333            SystemTime(ref e) => Some(e),
334            Other(_) => None,
335            General(_) => None,
336            Unknown => None,
337        }
338    }
339}
340
341impl From<io::Error> for Error {
342    fn from(e: io::Error) -> Error {
343        Error::IO(e)
344    }
345}
346
347impl From<std::time::SystemTimeError> for Error {
348    fn from(e: std::time::SystemTimeError) -> Error {
349        Error::SystemTime(e)
350    }
351}
352
353impl From<Box<dyn std::error::Error>> for Error {
354    fn from(e: Box<dyn std::error::Error>) -> Error {
355        Error::General(e.to_string())
356    }
357}
358
359extern "C" {
360    #[cfg(any(target_vendor = "apple", target_os = "windows"))]
361    fn get_os_type() -> *const i8;
362    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
363    fn get_os_release() -> *const i8;
364
365    #[cfg(all(not(any(target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd")), any(unix, windows)))]
366    fn get_cpu_num() -> u32;
367    #[cfg(any(all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))]
368    fn get_cpu_speed() -> u64;
369
370    #[cfg(target_os = "windows")]
371    fn get_loadavg() -> LoadAvg;
372    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku"))]
373    fn get_proc_total() -> u64;
374
375    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))]
376    fn get_mem_info() -> MemInfo;
377    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
378    fn get_mem_info_bsd(mi: &mut MemInfo) ->i32;
379
380    #[cfg(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "haiku"))]
381    fn get_disk_info() -> DiskInfo;
382    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
383    fn get_disk_info_bsd(di: &mut DiskInfo) -> i32;
384}
385
386
387/// Get operation system type.
388///
389/// Such as "Linux", "Darwin", "Windows".
390pub fn os_type() -> Result<String, Error> {
391    #[cfg(target_os = "linux")]
392    {
393        let mut s = String::new();
394        File::open("/proc/sys/kernel/ostype")?.read_to_string(&mut s)?;
395        s.pop(); // pop '\n'
396        Ok(s)
397    }
398    #[cfg(any(target_vendor = "apple", target_os = "windows"))]
399    {
400        let typ = unsafe { ffi::CStr::from_ptr(get_os_type() as *const c_char).to_bytes() };
401        Ok(String::from_utf8_lossy(typ).into_owned())
402    }
403    #[cfg(target_os = "solaris")]
404    {
405        Ok("solaris".to_string())
406    }
407    #[cfg(target_os = "illumos")]
408    {
409        Ok("illumos".to_string())
410    }
411    #[cfg(target_os = "freebsd")]
412    {
413        Ok("freebsd".to_string())
414    }
415    #[cfg(target_os = "openbsd")]
416    {
417        Ok("openbsd".to_string())
418    }
419    #[cfg(target_os = "netbsd")]
420    {
421        Ok("netbsd".to_string())
422    }
423    #[cfg(target_os = "haiku")]
424    {
425        Ok("haiku".to_string())
426    }
427    #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
428    {
429        Err(Error::UnsupportedSystem)
430    }
431}
432
433/// Get operation system release version.
434///
435/// Such as "3.19.0-gentoo"
436pub fn os_release() -> Result<String, Error> {
437    #[cfg(target_os = "linux")]
438    {
439        let mut s = String::new();
440        File::open("/proc/sys/kernel/osrelease")?.read_to_string(&mut s)?;
441        s.pop(); // pop '\n'
442        Ok(s)
443    }
444    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
445    {
446        unsafe {
447	    let rp = get_os_release() as *const c_char;
448	    if rp == std::ptr::null() {
449		Err(Error::Unknown)
450	    } else {
451		let typ = ffi::CStr::from_ptr(rp).to_bytes();
452		Ok(String::from_utf8_lossy(typ).into_owned())
453	    }
454	}
455    }
456    #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "haiku"))]
457    {
458        let release: Option<String> = unsafe {
459            let mut name: libc::utsname = std::mem::zeroed();
460            if libc::uname(&mut name) < 0 {
461                None
462            } else {
463                let cstr = std::ffi::CStr::from_ptr(name.release.as_mut_ptr());
464                Some(cstr.to_string_lossy().to_string())
465            }
466        };
467        match release {
468            None => Err(Error::Unknown),
469            Some(release) => Ok(release),
470        }
471    }
472    #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
473    {
474        Err(Error::UnsupportedSystem)
475    }
476}
477
478/// Get the os release note of Linux
479///
480/// Information in /etc/os-release, such as name and version of distribution.
481///
482/// See `LinuxOSReleaseInfo` for more documentation.
483pub fn linux_os_release() -> Result<LinuxOSReleaseInfo, Error> {
484    if !cfg!(target_os = "linux") {
485        return Err(Error::UnsupportedSystem);
486    }
487
488    let mut s = String::new();
489    File::open("/etc/os-release")?.read_to_string(&mut s)?;
490
491    let mut info: LinuxOSReleaseInfo = Default::default();
492    for l in s.split('\n') {
493        match parse_line_for_linux_os_release(l.trim().to_string()) {
494            Some((key, value)) =>
495                match (key.as_ref(), value) {
496                    ("ID", val) => info.id = Some(val),
497                    ("ID_LIKE", val) => info.id_like = Some(val),
498                    ("NAME", val) => info.name = Some(val),
499                    ("PRETTY_NAME", val) => info.pretty_name = Some(val),
500
501                    ("VERSION", val) => info.version = Some(val),
502                    ("VERSION_ID", val) => info.version_id = Some(val),
503                    ("VERSION_CODENAME", val) => info.version_codename = Some(val),
504
505                    ("ANSI_COLOR", val) => info.ansi_color = Some(val),
506                    ("LOGO", val) => info.logo = Some(val),
507
508                    ("CPE_NAME", val) => info.cpe_name = Some(val),
509                    ("BUILD_ID", val) => info.build_id = Some(val),
510                    ("VARIANT", val) => info.variant = Some(val),
511                    ("VARIANT_ID", val) => info.variant_id = Some(val),
512
513                    ("HOME_URL", val) => info.home_url = Some(val),
514                    ("BUG_REPORT_URL", val) => info.bug_report_url = Some(val),
515                    ("SUPPORT_URL", val) => info.support_url = Some(val),
516                    ("DOCUMENTATION_URL", val) => info.documentation_url = Some(val),
517                    ("PRIVACY_POLICY_URL", val) => info.privacy_policy_url = Some(val),
518                    _ => {}
519                }
520            None => {}
521        }
522    }
523
524    Ok(info)
525}
526
527fn parse_line_for_linux_os_release(l: String) -> Option<(String, String)> {
528    let words: Vec<&str> = l.splitn(2, '=').collect();
529    if words.len() < 2 {
530        return None
531    }
532    let mut trim_value = String::from(words[1]);
533
534    if trim_value.starts_with('"') {
535        trim_value.remove(0);
536    }
537    if trim_value.ends_with('"') {
538        let len = trim_value.len();
539        trim_value.remove(len - 1);
540    }
541
542    return Some((String::from(words[0]), trim_value))
543}
544
545/// Get cpu num quantity.
546///
547/// Notice, it returns the logical cpu quantity.
548pub fn cpu_num() -> Result<u32, Error> {
549    #[cfg(any(target_os = "solaris", target_os = "illumos", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
550    {
551        let ret = unsafe { libc::sysconf(libc::_SC_NPROCESSORS_ONLN) };
552        if ret < 1 || ret as i64 > std::u32::MAX as i64 {
553            Err(Error::IO(io::Error::last_os_error()))
554        } else {
555            Ok(ret as u32)
556        }
557    }
558    #[cfg(all(not(any(target_os = "solaris", target_os = "illumos", target_os="freebsd", target_os = "openbsd", target_os = "netbsd")), any(unix, windows)))]
559    {
560        unsafe { Ok(get_cpu_num()) }
561    }
562    #[cfg(not(any(target_os = "solaris", target_os = "illumos", unix, windows)))]
563    {
564        Err(Error::UnsupportedSystem)
565    }
566}
567
568/// Get cpu speed.
569///
570/// Such as 2500, that is 2500 MHz.
571pub fn cpu_speed() -> Result<u64, Error> {
572    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
573    {
574       Ok(kstat::cpu_mhz()?)
575    }
576    #[cfg(target_os = "linux")]
577    {
578        // /sys/devices/system/cpu/cpu0/cpufreq/cpuinfo_cur_freq
579        let mut s = String::new();
580        File::open("/proc/cpuinfo")?.read_to_string(&mut s)?;
581
582        let find_cpu_mhz = s.split('\n').find(|line|
583            line.starts_with("cpu MHz\t") ||
584                line.starts_with("BogoMIPS") ||
585                line.starts_with("clock\t") ||
586                line.starts_with("bogomips per cpu")
587        );
588
589        find_cpu_mhz.and_then(|line| line.split(':').last())
590            .and_then(|val| val.replace("MHz", "").trim().parse::<f64>().ok())
591            .map(|speed| speed as u64)
592            .ok_or(Error::Unknown)
593    }
594    #[cfg(any(all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "haiku"))]
595    {
596        unsafe { Ok(get_cpu_speed()) }
597    }
598    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
599    {
600	let res: u64 = unsafe { get_cpu_speed() };
601	match res {
602	    0 => Err(Error::IO(io::Error::last_os_error())),
603	    _ => Ok(res),
604	}
605    }
606    #[cfg(not(any(target_os = "solaris", target_os = "illumos", target_os = "linux", all(target_vendor = "apple", not(any(target_arch = "aarch64", target_arch = "arm"))), target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
607    {
608        Err(Error::UnsupportedSystem)
609    }
610}
611
612/// Get system load average value.
613///
614/// Notice, on windows, one/five/fifteen of the LoadAvg returned are the current load.
615pub fn loadavg() -> Result<LoadAvg, Error> {
616    #[cfg(target_os = "linux")]
617    {
618        let mut s = String::new();
619        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
620        let loads = s.trim().split(' ')
621            .take(3)
622            .map(|val| val.parse::<f64>().unwrap())
623            .collect::<Vec<f64>>();
624        Ok(LoadAvg {
625            one: loads[0],
626            five: loads[1],
627            fifteen: loads[2],
628        })
629    }
630    #[cfg(any(target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
631    {
632        let mut l: [c_double; 3] = [0f64; 3];
633        if unsafe { libc::getloadavg(l.as_mut_ptr(), l.len() as c_int) } < 3 {
634            Err(Error::Unknown)
635        } else {
636            Ok(LoadAvg {
637                one: l[0],
638                five: l[1],
639                fifteen: l[2],
640            })
641        }
642    }
643    #[cfg(any(target_os = "windows"))]
644    {
645        Ok(unsafe { get_loadavg() })
646    }
647    #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd")))]
648    {
649        Err(Error::UnsupportedSystem)
650    }
651}
652
653/// Get current processes quantity.
654pub fn proc_total() -> Result<u64, Error> {
655    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
656    {
657        Ok(kstat::nproc()?)
658    }
659    #[cfg(target_os = "linux")]
660    {
661        let mut s = String::new();
662        File::open("/proc/loadavg")?.read_to_string(&mut s)?;
663        s.split(' ')
664            .nth(3)
665            .and_then(|val| val.split('/').last())
666            .and_then(|val| val.parse::<u64>().ok())
667            .ok_or(Error::Unknown)
668    }
669    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))]
670    {
671        Ok(unsafe { get_proc_total() })
672    }
673    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
674    {
675	let res: u64 = unsafe { get_proc_total() };
676	match res {
677	    0 => Err(Error::IO(io::Error::last_os_error())),
678	    _ => Ok(res),
679	}
680    }
681    #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
682    {
683        Err(Error::UnsupportedSystem)
684    }
685}
686
687#[cfg(any(target_os = "solaris", target_os = "illumos"))]
688fn pagesize() -> Result<u32, Error> {
689    let ret = unsafe { libc::sysconf(libc::_SC_PAGESIZE) };
690    if ret < 1 || ret > std::u32::MAX as i64 {
691        Err(Error::Unknown)
692    } else {
693        Ok(ret as u32)
694    }
695}
696
697/// Get memory information.
698///
699/// On Mac OS X and Windows, the buffers and cached variables of the MemInfo returned are zero.
700pub fn mem_info() -> Result<MemInfo, Error> {
701    #[cfg(target_os = "linux")]
702    {
703        let mut s = String::new();
704        File::open("/proc/meminfo")?.read_to_string(&mut s)?;
705        let mut meminfo_hashmap = HashMap::new();
706        for line in s.lines() {
707            let mut split_line = line.split_whitespace();
708            let label = split_line.next();
709            let value = split_line.next();
710            if value.is_some() && label.is_some() {
711                let label = label.unwrap().split(':').nth(0).ok_or(Error::Unknown)?;
712                let value = value.unwrap().parse::<u64>().ok().ok_or(Error::Unknown)?;
713                meminfo_hashmap.insert(label, value);
714            }
715        }
716        let total = *meminfo_hashmap.get("MemTotal").ok_or(Error::Unknown)?;
717        let free = *meminfo_hashmap.get("MemFree").ok_or(Error::Unknown)?;
718        let buffers = *meminfo_hashmap.get("Buffers").ok_or(Error::Unknown)?;
719        let cached = *meminfo_hashmap.get("Cached").ok_or(Error::Unknown)?;
720        let avail = meminfo_hashmap.get("MemAvailable").map(|v| v.clone()).or_else(|| {
721            let sreclaimable = *meminfo_hashmap.get("SReclaimable")?;
722            let shmem = *meminfo_hashmap.get("Shmem")?;
723            Some(free + buffers + cached + sreclaimable - shmem)
724        }).ok_or(Error::Unknown)?;
725        let swap_total = *meminfo_hashmap.get("SwapTotal").ok_or(Error::Unknown)?;
726        let swap_free = *meminfo_hashmap.get("SwapFree").ok_or(Error::Unknown)?;
727        Ok(MemInfo {
728            total,
729            free,
730            avail,
731            buffers,
732            cached,
733            swap_total,
734            swap_free,
735        })
736    }
737    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
738    {
739        let pagesize = pagesize()? as u64;
740        let pages = kstat::pages()?;
741        return Ok(MemInfo {
742            total: pages.physmem * pagesize / 1024,
743            avail: 0,
744            free: pages.freemem * pagesize / 1024,
745            cached: 0,
746            buffers: 0,
747            swap_total: 0,
748            swap_free: 0,
749        });
750    }
751    #[cfg(any(target_vendor = "apple", target_os = "windows", target_os = "haiku"))]
752    {
753        Ok(unsafe { get_mem_info() })
754    }
755    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
756    {
757	let mut mi:MemInfo = MemInfo{total: 0, free: 0, avail: 0, buffers: 0,
758				     cached: 0, swap_total: 0, swap_free: 0};
759	let res: i32 = unsafe { get_mem_info_bsd(&mut mi) };
760	match res {
761	    -1 => Err(Error::IO(io::Error::last_os_error())),
762	    0 => Ok(mi),
763	    _ => Err(Error::Unknown),
764	}
765    }
766    #[cfg(not(any(target_os = "linux", target_os = "solaris", target_os = "illumos", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
767    {
768        Err(Error::UnsupportedSystem)
769    }
770}
771
772/// Get disk information.
773///
774/// Notice, it just calculate current disk on Windows.
775pub fn disk_info() -> Result<DiskInfo, Error> {
776    #[cfg(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "haiku"))]
777    {
778        Ok(unsafe { get_disk_info() })
779    }
780    #[cfg(any(target_os = "freebsd", target_os = "openbsd", target_os = "netbsd"))]
781    {
782	let mut di:DiskInfo = DiskInfo{total: 0, free: 0};
783	let res: i32 = unsafe { get_disk_info_bsd(&mut di) };
784	match res {
785	    -1 => Err(Error::IO(io::Error::last_os_error())),
786	    0 => Ok(di),
787	    _ => Err(Error::Unknown),
788	}
789    }
790    #[cfg(not(any(target_os = "linux", target_vendor = "apple", target_os = "windows", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd", target_os = "haiku")))]
791    {
792        Err(Error::UnsupportedSystem)
793    }
794}
795
796/// Get hostname.
797#[cfg(target_family = "unix")]
798pub fn hostname() -> Result<String, Error> {
799    unsafe {
800        let buf_size = libc::sysconf(libc::_SC_HOST_NAME_MAX) as usize;
801        let mut buf = Vec::<u8>::with_capacity(buf_size + 1);
802        if libc::gethostname(buf.as_mut_ptr() as *mut libc::c_char, buf_size) < 0 {
803            return Err(Error::IO(io::Error::last_os_error()));
804        }
805        let hostname_len = libc::strnlen(buf.as_ptr() as *const libc::c_char, buf_size);
806        buf.set_len(hostname_len);
807        Ok(ffi::CString::new(buf).unwrap().into_string().unwrap())
808    }
809}
810
811#[cfg(target_family = "windows")]
812pub fn hostname() -> Result<String, Error> {
813    use std::process::Command;
814    Command::new("hostname")
815        .output()
816        .map_err(Error::ExecFailed)
817        .map(|output| String::from_utf8(output.stdout).unwrap().trim().to_string())
818}
819
820/// Get system boottime
821#[cfg(not(windows))]
822pub fn boottime() -> Result<timeval, Error> {
823    let mut bt = timeval {
824        tv_sec: 0,
825        tv_usec: 0
826    };
827
828    #[cfg(any(target_os = "linux", target_os="android"))]
829    {
830        let mut s = String::new();
831        File::open("/proc/uptime")?.read_to_string(&mut s)?;
832        let secs = s.trim().split(' ')
833            .take(2)
834            .map(|val| val.parse::<f64>().unwrap())
835            .collect::<Vec<f64>>();
836        bt.tv_sec = secs[0] as libc::time_t;
837        bt.tv_usec = secs[1] as libc::suseconds_t;
838	    return Ok(bt);
839    }
840    #[cfg(any(target_vendor = "apple", target_os="freebsd", target_os = "openbsd", target_os = "netbsd"))]
841    {
842        let mut mib = [OS_CTL_KERN, OS_KERN_BOOTTIME];
843        let mut size: libc::size_t = size_of_val(&bt) as libc::size_t;
844        unsafe {
845            if sysctl(&mut mib[0], 2,
846                   &mut bt as *mut timeval as *mut libc::c_void,
847                   &mut size, null_mut(), 0) == -1 {
848                return Err(Error::IO(io::Error::last_os_error()));
849            } else {
850                return Ok(bt);
851            }
852        }
853    }
854    #[cfg(any(target_os = "solaris", target_os = "illumos"))]
855    {
856        let start = kstat::boot_time()?;
857        let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
858        let now = now.as_secs();
859        if now < start {
860            return Err(Error::General("time went backwards".into()));
861        }
862        bt.tv_sec = (now - start) as i64;
863	    return Ok(bt);
864    }
865
866    #[cfg(target_os = "haiku")]
867    {
868        unsafe {
869            let mut sysinfo: libc::system_info = std::mem::zeroed();
870            if libc::get_system_info(&mut sysinfo) == libc::B_OK {
871                let mut now: libc::time_t = 0;
872                libc::time(&mut now);
873                bt.tv_sec = now - (sysinfo.boot_time / 1000000);
874                bt.tv_usec = (sysinfo.boot_time % 1000000) as libc::suseconds_t;
875                return Ok(bt);
876            }
877        }
878        return Err(Error::IO(io::Error::last_os_error()));
879    }
880
881    #[warn(unreachable_code)]
882    Err(Error::UnsupportedSystem)
883}
884
885
886/// a type that includes all (probably) informations that windows provide about your graphics card.
887#[derive(Debug)]
888pub struct WindowsGraphicsCard {
889    pub name: Vec<String>,
890    pub description: Vec<String>,
891    pub caption: Vec<String>,
892    pub status: Vec<String>,
893    pub status_info: Vec<String>,
894    pub availability: Vec<String>,
895    pub driver_version: Vec<String>,
896    pub adapter_ram: Vec<String>,
897    pub adapter_dac_type: Vec<String>,
898    pub current_refresh_rate: Vec<String>,
899    pub max_refresh_rate: Vec<String>,
900    pub min_refresh_rate: Vec<String>,
901    pub current_bits_per_pixel: Vec<String>,
902    pub current_horizontal_resolution: Vec<String>,
903    pub current_vertical_resolution: Vec<String>,
904    pub current_number_of_colors: Vec<String>,
905    pub current_number_of_columns: Vec<String>,
906    pub current_number_of_rows: Vec<String>,
907    pub current_scan_mode: Vec<String>,
908    pub device_id: Vec<String>,
909    pub dither_type: Vec<String>,
910    pub driver_date: Vec<String>,
911    pub icm_intent: Vec<String>,
912    pub icm_method: Vec<String>,
913    pub inf_file_name: Vec<String>,
914    pub inf_section: Vec<String>,
915    pub install_date: Vec<String>,
916    pub installed_display_drivers: Vec<String>,
917    pub max_memory_supported: Vec<String>,
918    pub max_number_controlled: Vec<String>,
919    pub monochrome: Vec<String>,
920    pub number_of_color_planes: Vec<String>,
921    pub number_of_video_pages: Vec<String>,
922    pub pnp_device_id: Vec<String>,
923    pub power_management_capabilities: Vec<String>,
924    pub power_management_supported: Vec<String>,
925    pub protocol_supported: Vec<String>,
926    pub reserved_system_palette_entries: Vec<String>,
927    pub specification_version: Vec<String>,
928    pub system_creation_classname: Vec<String>,
929    pub system_name: Vec<String>,
930    pub system_palette_entries: Vec<String>,
931    pub time_of_last_reset: Vec<String>,
932    pub video_architecture: Vec<String>,
933    pub video_memory_type: Vec<String>,
934    pub video_mode: Vec<String>,
935    pub video_mode_description: Vec<String>,
936    pub video_processor: Vec<String>,
937    pub accelerator_capabilities: Vec<String>,
938    pub capability_descriptions: Vec<String>,
939    pub color_table_entries: Vec<String>,
940    pub config_manager_error_code: Vec<String>,
941    pub config_manager_user_config: Vec<String>,
942    pub creation_classname: Vec<String>
943}
944
945/// get the graphic card infos, for windows.
946pub fn get_graphics_info() -> std::result::Result<WindowsGraphicsCard, std::io::Error> {
947    if !cfg!(target_os = "windows") {
948        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'get_graphics_info()' function is only available on windows."));
949    }
950    use std::process::{Command, Output};
951    use std::str;
952
953    let name = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("name").output().unwrap();
954    let driver_version = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("driverversion").output().unwrap();
955    let adapter_ram = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("adapterram").output().unwrap();
956    let video_mode_description = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("VideoModeDescription").output().unwrap();
957    let current_refresh_rate = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentRefreshRate").output().unwrap();
958    let current_bits_per_pixel = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentBitsPerPixel").output().unwrap();
959    let current_number_of_colors = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentNumberOfColors").output().unwrap();
960    let current_number_of_columns = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentNumberOfColumns").output().unwrap();
961    let current_number_of_rows = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentNumberOfRows").output().unwrap();
962    let current_horizontal_resolution = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentHorizontalResolution").output().unwrap();
963    let current_vertical_resolution = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentVerticalResolution").output().unwrap();
964    let current_scan_mode = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CurrentScanMode").output().unwrap();
965    let device_id = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("DeviceID").output().unwrap();
966    let dither_type = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("DitherType").output().unwrap();
967    let driver_date = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("DriverDate").output().unwrap();
968    let icm_intent = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ICMIntent").output().unwrap();
969    let icm_method = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ICMMethod").output().unwrap();
970    let inf_file_name = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("InfFilename").output().unwrap();
971    let inf_section = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("InfSection").output().unwrap();
972    let install_date = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("InstallDate").output().unwrap();
973    let installed_display_drivers = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("InstalledDisplayDrivers").output().unwrap();
974    let max_memory_supported = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("MaxMemorySupported").output().unwrap();
975    let max_number_controlled = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("MaxNumberControlled").output().unwrap();
976    let max_refresh_rate = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("MaxRefreshRate").output().unwrap();
977    let min_refresh_rate = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("MinRefreshRate").output().unwrap();
978    let monochrome = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("Monochrome").output().unwrap();
979    let number_of_color_planes = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("NumberOfColorPlanes").output().unwrap();
980    let number_of_video_pages = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("NumberOfVideoPages").output().unwrap();
981    let pnp_device_id = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("PNPDeviceID").output().unwrap();
982    let power_management_capabilities = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("PowerManagementCapabilities").output().unwrap();
983    let power_management_supported = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("PowerManagementSupported").output().unwrap();
984    let description = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("Description").output().unwrap();
985    let protocol_supported = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ProtocolSupported").output().unwrap();
986    let reserved_system_palette_entries = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ReservedSystemPaletteEntries").output().unwrap();
987    let specification_version = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("SpecificationVersion").output().unwrap();
988    let status = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("Status").output().unwrap();
989    let status_info = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("StatusInfo").output().unwrap();
990    let system_creation_classname = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("SystemCreationClassName").output().unwrap();
991    let system_name = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("SystemName").output().unwrap();
992    let system_palette_entries = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("SystemPaletteEntries").output().unwrap();
993    let time_of_last_reset = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("TimeOfLastReset").output().unwrap();
994    let video_architecture = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("VideoArchitecture").output().unwrap();
995    let video_memory_type = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("VideoMemoryType").output().unwrap();
996    let video_mode = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("VideoMode").output().unwrap();
997    let video_processor = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("VideoProcessor").output().unwrap();
998    let accelerator_capabilities = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("AcceleratorCapabilities").output().unwrap();
999    let adapter_dac_type = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("AdapterDACType").output().unwrap();
1000    let availability = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("Availability").output().unwrap();
1001    let capability_descriptions = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CapabilityDescriptions").output().unwrap();
1002    let caption = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("Caption").output().unwrap();
1003    let color_table_entries = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ColorTableEntries").output().unwrap();
1004    let config_manager_error_code = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ConfigManagerErrorCode").output().unwrap();
1005    let config_manager_user_config = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("ConfigManagerUserConfig").output().unwrap();
1006    let creation_classname = Command::new("wmic").arg("path").arg("win32_VideoController").arg("get").arg("CreationClassName").output().unwrap();
1007
1008    fn windows_outputs_cleanup(output: Output) -> Vec<String> {
1009        let parse_output = str::from_utf8(&output.stdout).unwrap();
1010
1011        let mut new_string_array = vec![];
1012
1013        for (index, line) in parse_output.lines().enumerate() {
1014            if index != 0 && line != "" {
1015                let line = line.replace("\r", "").trim().to_string();
1016
1017                if line != "".to_string() {
1018                    new_string_array.push(line.to_string())
1019                }
1020            }
1021        }
1022
1023        return new_string_array;
1024    }
1025
1026    Ok(WindowsGraphicsCard {
1027        accelerator_capabilities: windows_outputs_cleanup(accelerator_capabilities),
1028        name: windows_outputs_cleanup(name), 
1029        description: windows_outputs_cleanup(description),
1030        caption: windows_outputs_cleanup(caption),
1031        driver_version: windows_outputs_cleanup(driver_version), 
1032        availability: windows_outputs_cleanup(availability),
1033        adapter_ram: windows_outputs_cleanup(adapter_ram),
1034        adapter_dac_type: windows_outputs_cleanup(adapter_dac_type),  
1035        current_refresh_rate: windows_outputs_cleanup(current_refresh_rate),
1036        max_refresh_rate: windows_outputs_cleanup(max_refresh_rate),
1037        min_refresh_rate: windows_outputs_cleanup(min_refresh_rate),
1038        current_bits_per_pixel: windows_outputs_cleanup(current_bits_per_pixel), 
1039        current_number_of_colors: windows_outputs_cleanup(current_number_of_colors), 
1040        current_number_of_columns: windows_outputs_cleanup(current_number_of_columns), 
1041        current_number_of_rows: windows_outputs_cleanup(current_number_of_rows), 
1042        current_horizontal_resolution: windows_outputs_cleanup(current_horizontal_resolution),
1043        current_vertical_resolution: windows_outputs_cleanup(current_vertical_resolution),
1044        current_scan_mode: windows_outputs_cleanup(current_scan_mode),
1045        device_id: windows_outputs_cleanup(device_id),
1046        dither_type: windows_outputs_cleanup(dither_type),
1047        driver_date: windows_outputs_cleanup(driver_date),
1048        icm_intent: windows_outputs_cleanup(icm_intent),
1049        icm_method: windows_outputs_cleanup(icm_method),
1050        inf_file_name: windows_outputs_cleanup(inf_file_name),
1051        inf_section: windows_outputs_cleanup(inf_section),
1052        install_date: windows_outputs_cleanup(install_date),
1053        installed_display_drivers: windows_outputs_cleanup(installed_display_drivers),
1054        max_memory_supported: windows_outputs_cleanup(max_memory_supported),
1055        max_number_controlled: windows_outputs_cleanup(max_number_controlled),
1056        monochrome: windows_outputs_cleanup(monochrome),
1057        number_of_color_planes: windows_outputs_cleanup(number_of_color_planes),
1058        number_of_video_pages: windows_outputs_cleanup(number_of_video_pages),
1059        pnp_device_id: windows_outputs_cleanup(pnp_device_id),
1060        power_management_capabilities: windows_outputs_cleanup(power_management_capabilities),
1061        power_management_supported: windows_outputs_cleanup(power_management_supported),
1062        protocol_supported: windows_outputs_cleanup(protocol_supported),
1063        reserved_system_palette_entries: windows_outputs_cleanup(reserved_system_palette_entries),
1064        specification_version: windows_outputs_cleanup(specification_version),
1065        status: windows_outputs_cleanup(status),
1066        status_info: windows_outputs_cleanup(status_info),
1067        system_creation_classname: windows_outputs_cleanup(system_creation_classname),
1068        system_name: windows_outputs_cleanup(system_name),
1069        system_palette_entries: windows_outputs_cleanup(system_palette_entries),
1070        time_of_last_reset: windows_outputs_cleanup(time_of_last_reset),
1071        video_architecture: windows_outputs_cleanup(video_architecture),
1072        video_memory_type: windows_outputs_cleanup(video_memory_type),
1073        video_mode: windows_outputs_cleanup(video_mode),
1074        video_mode_description: windows_outputs_cleanup(video_mode_description),
1075        video_processor: windows_outputs_cleanup(video_processor),
1076        capability_descriptions: windows_outputs_cleanup(capability_descriptions),
1077        color_table_entries: windows_outputs_cleanup(color_table_entries),
1078        config_manager_error_code: windows_outputs_cleanup(config_manager_error_code),
1079        config_manager_user_config: windows_outputs_cleanup(config_manager_user_config),
1080        creation_classname: windows_outputs_cleanup(creation_classname)
1081    })
1082}
1083
1084/// get the computer type. Only "Notebook" and "Desktop" allowed for linux, checks if a battery is exist that implemented on your computer and if it exists, return "Notebook" value, otherwise "Desktop" value. But the way it work on windows is different, it can return various values since windows has able to give more specific infos about computer types. 
1085#[cfg(any(target_os = "windows", target_os = "linux"))]
1086pub fn check_computer_type<'a>() -> std::result::Result<&'a str, Error> {
1087    use std::process::{Command, Output};
1088    use std::str;
1089    let mut result = "Unknown";
1090
1091    #[cfg(target_os = "windows")]
1092    {
1093        fn computer_type_cleanup_for_windows<'a>(our_output: Output) -> &'a str {
1094            let get_output = str::from_utf8(&our_output.stdout).unwrap()
1095                                            .replace("\r", "")
1096                                            .replace("\n", "")
1097                                            .replace("chassistypes", "")
1098                                            .replace("-", "")
1099                                            .replace(" ", "");
1100    
1101            return match get_output.as_str() {
1102                "{1}" => "Other", "{2}" => "Unknown", "{3}" => "Desktop", "{4}" => "Low Profile Desktop", "{5}" => "Pizza Box",
1103                "{6}" => "Mini Tower", "{7}" => "Tower", "{8}" => "Portable", "{9}" => "Laptop", "{10}" => "Notebook", "{11}" => "Handheld", 
1104                "{12}" => "Docking Station", "{13}" => "All-in-One", "{14}" => "Sub-Notebook", "{15}" => "Space Saving", "{16}" => "Lunch Box", 
1105                "{17}" => "Main System Chassis", "{18}" => "Expansion Chassis", "{19}" => "Sub-Chassis", "{20}" => "Bus Expansion Chassis", 
1106                "{21}" => "Peripheral Chassis", "{22}" => "Storage Chassis", "{23}" => "Rack Mount Chassis", "{24}" => "Sealed-Case PC",
1107                &_ => "Unknown" 
1108            }
1109        }
1110    
1111        let chassis_type_number = Command::new("powershell")
1112                                            .arg("Get-WmiObject")
1113                                            .arg("win32_systemenclosure | select chassistypes")
1114                                            .output();
1115    
1116        match chassis_type_number {
1117            Ok(chassis_num) => result = computer_type_cleanup_for_windows(chassis_num),
1118            Err(err) => return Err(Error::ExecFailed(err))
1119        };
1120
1121        
1122    };
1123
1124    #[cfg(target_os = "linux")]
1125    {
1126        let check_bat0 = Command::new("sh")
1127                            .arg("-c")
1128                            .arg("test -d /sys/class/power_supply/BAT0")
1129                            .output();
1130
1131
1132        let check_bat0 = match check_bat0 {
1133            Ok(bat) => bat.status.success(),
1134            Err(error) => return Err(Error::ExecFailed(error))
1135        };
1136
1137        let check_bat1 = Command::new("sh")
1138                            .arg("-c")
1139                            .arg("test -d /sys/class/power_supply/BAT1")
1140                            .output();
1141
1142        let check_bat1 = match check_bat1 {
1143            Ok(bat) => bat.status.success(),
1144            Err(error) => return Err(Error::ExecFailed(error))
1145        };
1146
1147        let check_bat2 = Command::new("sh")
1148                            .arg("-c")
1149                            .arg("test -d /sys/class/power_supply/BAT2")
1150                            .output();
1151
1152        let check_bat2 = match check_bat2 {
1153            Ok(bat) => bat.status.success(),
1154            Err(error) => return Err(Error::ExecFailed(error))
1155        };
1156
1157        let check_bat3 = Command::new("sh")
1158                            .arg("-c")
1159                            .arg("test -d /sys/class/power_supply/BAT3")
1160                            .output();
1161
1162        let check_bat3 = match check_bat3 {
1163            Ok(bat) => bat.status.success(),
1164            Err(error) => return Err(Error::ExecFailed(error))
1165        };
1166
1167        if check_bat0 || check_bat1 || check_bat2 || check_bat3 {
1168            result = "Notebook";
1169        } else {
1170            result = "Desktop";
1171        }
1172    }
1173
1174    return Ok(result);
1175}
1176
1177
1178/// get the current user as string. Both works on windows and linux.
1179#[cfg(any(target_os = "windows", target_os = "linux"))]
1180pub fn get_current_user() -> String {
1181    use std::process::Command;
1182    use std::str::from_utf8;
1183    let result;
1184
1185    #[cfg(target_os = "windows")]
1186    {
1187        let current_user_command = Command::new("cmd")
1188                                        .arg("/C")
1189                                        .arg("echo")
1190                                        .arg("%username%")
1191                                        .output()
1192                                        .expect("cannot figured out user");
1193
1194        result = from_utf8(&current_user_command.stdout).unwrap().trim().to_string()
1195    }
1196
1197    #[cfg(target_os = "linux")]
1198    {
1199        let current_user_command = Command::new("whoami")
1200                                                .output()
1201                                                .expect("cannot figured out user");
1202
1203        result = from_utf8(&current_user_command.stdout).unwrap().trim().to_string()
1204    }
1205
1206    return result
1207}
1208
1209/// Get the public ipv4 address as string.
1210#[cfg(any(target_os = "windows", target_os = "linux"))]
1211pub fn get_public_ipv4_address() -> std::result::Result<String, Error> {
1212    let mut ip_address = String::new();
1213
1214    #[cfg(target_os = "linux")]
1215    {
1216        extern crate palin;
1217        use palin::{check_if_curl_exist, check_if_dig_exist, check_if_wget_exist};
1218
1219        if check_if_dig_exist() {
1220            let dig_command = std::process::Command::new("dig").arg("+short").arg("myip.opendns.com").arg("@resolver1.opendns.com").output();
1221
1222            match dig_command {
1223                Ok(answer) => {
1224                    let parse_answer = std::str::from_utf8(&answer.stdout).unwrap();
1225
1226                    ip_address = parse_answer.trim().to_string();
1227                },
1228                Err(error) => return Err(Error::Other(error.to_string()))
1229            }
1230        } else if check_if_wget_exist() {
1231            let wget_command = std::process::Command::new("wget").arg("-qO-").arg("ifconfig.me/ip").output();
1232
1233            match wget_command {
1234                Ok(answer) => {
1235                    let parse_answer = std::str::from_utf8(&answer.stdout).unwrap();
1236
1237                    ip_address = parse_answer.trim().to_string();
1238                },
1239                Err(error) => return Err(Error::Other(error.to_string()))
1240            }
1241        } else if check_if_curl_exist() {
1242            let curl_command = std::process::Command::new("curl").arg("ifconfig.me/ip").output();
1243
1244            match curl_command {
1245                Ok(answer) => {
1246                    let parse_answer = std::str::from_utf8(&answer.stdout).unwrap();
1247
1248                    ip_address = parse_answer.trim().to_string();
1249                },
1250                Err(error) => return Err(Error::Other(error.to_string()))
1251            }
1252        }
1253    }
1254
1255    #[cfg(target_os = "windows")]
1256    {
1257        let get_ip_address = std::process::Command::new("powershell").arg("-Command").arg("(Invoke-WebRequest -Uri \"http://ifconfig.me/ip\").Content").output();
1258
1259        match get_ip_address {
1260            Ok(answer) => {
1261                let parse_answer = std::str::from_utf8(&answer.stdout).unwrap();
1262
1263                ip_address = parse_answer.trim().to_string()
1264            },
1265            Err(error) => return Err(Error::Other(error.to_string()))
1266        }
1267    }
1268
1269    return Ok(ip_address)
1270}
1271
1272/// that function searchs a program on the terminal if it's exist and / or returns a positive answer to various version arguments. Works on both Windows And Linux.
1273pub fn is_program_installed(program: &str) -> bool {
1274    let check1 = std::process::Command::new(program).output();
1275
1276    match check1 {
1277        Ok(_) => return true,
1278        Err(_) => ()
1279    };
1280
1281    let check2 = std::process::Command::new(program).arg("version").output();
1282
1283    match check2 {
1284        Ok(_) => return true,
1285        Err(_) => ()
1286    };
1287
1288    let check3 = std::process::Command::new(program).arg("--version").output();
1289
1290    match check3 {
1291        Ok(_) => return true,
1292        Err(_) => ()
1293    }
1294
1295    let check4 = std::process::Command::new(program).arg("-version").output();
1296
1297    match check4 {
1298        Ok(_) => return true,
1299        Err(_) => ()
1300    }
1301
1302    let check5 = std::process::Command::new(program).arg("-v").output();
1303
1304    match check5 {
1305        Ok(_) => return true,
1306        Err(_) => ()
1307    }
1308
1309    return false
1310}
1311
1312
1313/// type that includes hard search options for `is_program_installed_search_hard()` function.
1314#[derive(Debug)]
1315pub struct HardSearchOptions{
1316    pub case_sensitive: bool,
1317    pub search_hardness: u8
1318}
1319
1320/// Since windows has a bunch of api's that enlists downloaded programs and not all of them reachable via a terminal, that function searchs a program with given name and options on both terminal and various program listing api's of windows. Warning: It runs too slow. Use it with caution.
1321pub fn is_program_installed_search_hard(program: &str, options: HardSearchOptions) -> std::result::Result<bool, std::io::Error> {
1322    if !cfg!(target_os = "windows") {
1323        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'is_program_installed_search_hard()' function is only available on windows."));
1324    }
1325
1326    if options.search_hardness == 0 {
1327        return Ok(false);
1328    }
1329
1330    let check1 = std::process::Command::new(program).output();
1331
1332    match check1 {
1333        Ok(_) => return Ok(true),
1334        Err(_) => ()
1335    };
1336
1337    if options.search_hardness == 1 {
1338        return Ok(false);
1339    }
1340
1341    let check2 = std::process::Command::new(program).arg("version").output();
1342
1343    match check2 {
1344        Ok(_) => return Ok(true),
1345        Err(_) => ()
1346    };
1347
1348    if options.search_hardness == 2 {
1349        return Ok(false);
1350    }
1351
1352    let check3 = std::process::Command::new(program).arg("--version").output();
1353
1354    match check3 {
1355        Ok(_) => return Ok(true),
1356        Err(_) => ()
1357    }
1358
1359    if options.search_hardness == 3 {
1360        return Ok(false);
1361    }
1362
1363    let check4 = std::process::Command::new("powershell").arg("Get-Package").output();
1364
1365    match check4 {
1366        Ok(answer) => {
1367            let parse_answer = String::from_utf8_lossy(&answer.stdout);
1368
1369            for line in parse_answer.lines() {
1370                if options.case_sensitive {
1371                    if line.trim().starts_with(program) {
1372                        return Ok(true);
1373                    } 
1374                } else {
1375                    let left_side = line.trim().to_lowercase();
1376                    let left_side = String::from_utf8_lossy(left_side.as_bytes());
1377                    let left_side = left_side.as_ref();
1378                    let right_side = program.to_lowercase();
1379                    let right_side = String::from_utf8_lossy(right_side.as_bytes());
1380                    let right_side = right_side.as_ref();
1381                    
1382                    if left_side.contains(right_side) {
1383                        return Ok(true)
1384                    }
1385                }
1386            }
1387        },
1388        Err(_) => ()
1389    }
1390
1391    if options.search_hardness == 4 {
1392        return Ok(false);
1393    }
1394
1395    let check5 = std::process::Command::new("powershell").arg("Get-AppxPackage").output();
1396
1397    match check5 {
1398        Ok(answer) => {
1399            let parse_answer = String::from_utf8_lossy(&answer.stdout);
1400
1401            for line in parse_answer.lines() {
1402                match options.case_sensitive {
1403                    true => {
1404                        if line.starts_with("Name") {
1405                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1406
1407                            if program == split_the_line {
1408                                return Ok(true);
1409                            }
1410                        }
1411
1412                        if line.starts_with("PackageFullName") {
1413                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1414
1415                            if program == split_the_line {
1416                                return Ok(true);
1417                            }
1418                        }
1419
1420                        if line.starts_with("PackageFamilyName") {
1421                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1422
1423                            if program == split_the_line {
1424                                return Ok(true);
1425                            }
1426                        }
1427                    },
1428                    false => {
1429                        if line.starts_with("Name") {
1430                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1431
1432                            let left_side = split_the_line.trim().to_lowercase();
1433                            let left_side = String::from_utf8_lossy(left_side.as_bytes());
1434                            let left_side = left_side.as_ref();
1435                            let right_side = program.to_lowercase();
1436                            let right_side = String::from_utf8_lossy(right_side.as_bytes());
1437                            let right_side = right_side.as_ref();
1438                            
1439                            if left_side == right_side {
1440                                return Ok(true)
1441                            }
1442                        }
1443
1444                        if line.starts_with("PackageFullName") {
1445                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1446
1447                            let left_side = split_the_line.trim().to_lowercase();
1448                            let left_side = String::from_utf8_lossy(left_side.as_bytes());
1449                            let left_side = left_side.as_ref();
1450                            let right_side = program.to_lowercase();
1451                            let right_side = String::from_utf8_lossy(right_side.as_bytes());
1452                            let right_side = right_side.as_ref();
1453                            
1454                            if left_side == right_side {
1455                                return Ok(true)
1456                            }
1457                        }
1458
1459                        if line.starts_with("PackageFamilyName") {
1460                            let split_the_line: &str = line.split(":").collect::<Vec<&str>>()[1].trim();
1461
1462                            let left_side = split_the_line.trim().to_lowercase();
1463                            let left_side = String::from_utf8_lossy(left_side.as_bytes());
1464                            let left_side = left_side.as_ref();
1465                            let right_side = program.to_lowercase();
1466                            let right_side = String::from_utf8_lossy(right_side.as_bytes());
1467                            let right_side = right_side.as_ref();
1468                            
1469                            if left_side == right_side {
1470                                return Ok(true)
1471                            }
1472                        }
1473                    }
1474                }
1475            }
1476        },
1477        Err(_) => ()
1478    }
1479
1480    if options.search_hardness == 5 {
1481        return Ok(false);
1482    }
1483
1484    let check6 = std::process::Command::new("powershell").arg("wmic").arg("product").arg("get").arg("name").output();
1485
1486    match check6 {
1487        Ok(answer) => {
1488            let parse_answer = String::from_utf8_lossy(&answer.stdout);
1489
1490            for line in parse_answer.lines() {
1491                if options.case_sensitive {
1492                    if line.trim() == program {
1493                        return Ok(true);
1494                    } 
1495                } else {
1496                    if line.trim().to_lowercase().as_bytes() == program.to_lowercase().as_bytes() {
1497                        return Ok(true)
1498                    }
1499                }
1500            }
1501        },
1502        Err(error) => {
1503            eprintln!("for that reason we cannot take wmic output: {}", error)
1504        }
1505    }
1506
1507    return Ok(false)
1508}
1509
1510/// type that includes mghz and ddr type values.
1511#[derive(Debug)]
1512pub struct RamInFo {
1513    pub mhz: i32,
1514    pub ddr_type: String
1515}
1516
1517/// returns the `RamInfo` struct per each ram that attached your computer, that includes the mhz value and ddr type, for only windows. 
1518pub fn get_ram_infos() -> std::result::Result<Vec<RamInFo>, std::io::Error> {
1519    if !cfg!(target_os = "windows") {
1520        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'get_ram_infos()' function is only available on windows."));
1521    }
1522
1523    let ram_info_command = std::process::Command::new("wmic").arg("memorychip").arg("get").arg("speed").output();
1524
1525    match ram_info_command {
1526        Ok(answer) => {
1527            let parse_the_answer = std::str::from_utf8(&answer.stdout).unwrap();
1528            let mut rams = vec![];
1529
1530            for (index, line) in parse_the_answer.lines().into_iter().enumerate() {
1531                let mut mhz: i32 = 0;
1532                let mut ddr_type: String = "".to_string();
1533
1534                if index == 0 || line.trim() == "" {
1535                    continue;
1536                }
1537
1538                mhz = line.trim().parse::<i32>().unwrap();
1539
1540                if mhz >= 200 && mhz <= 400 {
1541                    ddr_type = "ddr1".to_string();
1542                }
1543
1544                if mhz >= 400 && mhz <= 800 {
1545                    ddr_type = "ddr2".to_string();
1546                }
1547
1548                if mhz > 800 && mhz <= 1860 {
1549                    ddr_type = "ddr3".to_string();
1550                }
1551
1552                if mhz >= 2133 && mhz <= 3200 {
1553                    ddr_type = "ddr4".to_string();
1554                }
1555
1556                if mhz > 3200 && mhz <= 8400 {
1557                    ddr_type = "ddr5".to_string();
1558                }
1559
1560                let ram_info = RamInFo {
1561                    ddr_type, mhz
1562                };
1563
1564                rams.push(ram_info);
1565            }
1566
1567            return Ok(rams);
1568        },
1569        Err(error) => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error.to_string()))
1570    }
1571}
1572
1573/// gets value of the env that has given name from the system. only for windows. 
1574pub fn get_system_env_var(var_name: &str) -> std::result::Result<String, std::io::Error> {
1575    #[cfg(target_os = "windows")]
1576    {
1577        let sanitize_var_name = var_name.to_ascii_uppercase();
1578        let format_the_command = format!("[System.Environment]::GetEnvironmentVariable('{}', 'Machine')", sanitize_var_name);
1579
1580        let output = std::process::Command::new("powershell")
1581                        .arg("-Command")
1582                        .arg(format_the_command)
1583                        .output();
1584
1585        return match output {
1586            Ok(var_list) => Ok(String::from_utf8_lossy(&var_list.stdout).to_string()),
1587            Err(error) => Err(std::io::Error::new(std::io::ErrorKind::Other, error.to_string()))
1588        }
1589    }
1590
1591    #[cfg(target_os = "linux")]
1592    {
1593        let sanitize_var_name = var_name.to_ascii_uppercase();
1594
1595        let output = std::process::Command::new("printenv")
1596                        .arg(sanitize_var_name)
1597                        .output();
1598
1599        return match output {
1600            Ok(var_list) => Ok(String::from_utf8_lossy(&var_list.stdout).to_string()),
1601            Err(error) => Err(std::io::Error::new(std::io::ErrorKind::Other, error.to_string()))
1602        }      
1603    }
1604}
1605
1606/// gets value of the env that has given name from the user. only for windows.
1607pub fn get_user_env_var(var_name: &str) -> std::result::Result<String, std::io::Error> {
1608    if !cfg!(target_os = "windows") {
1609        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'get_user_env_var()' function is only available on windows."));
1610    }
1611
1612    let sanitize_var_name = var_name.to_ascii_uppercase();
1613    let format_the_command = format!("[System.Environment]::GetEnvironmentVariable('{}', 'User')", sanitize_var_name);
1614
1615    let output = std::process::Command::new("powershell")
1616                    .arg("-Command")
1617                    .arg(format_the_command)
1618                    .output();
1619
1620    return match output {
1621        Ok(var_list) => Ok(String::from_utf8_lossy(&var_list.stdout).to_string()),
1622        Err(error) => Err(std::io::Error::new(std::io::ErrorKind::Other, error.to_string()))
1623    }
1624}
1625
1626#[cfg(target_os = "windows")]
1627#[derive(Debug)]
1628pub struct LanguageOptions {
1629   pub country: String,
1630   pub name: String,
1631   pub shortening: String,
1632   pub lcid: String
1633}
1634
1635#[cfg(target_os = "linux")]
1636#[derive(Debug)]
1637pub struct LanguageOptions {
1638   pub shortening: String,
1639   pub character_encoding: String,
1640   pub country: String,
1641}
1642
1643/// returns the language options for the computer, both works on windows and linux.
1644pub fn get_language_options() -> std::result::Result<LanguageOptions, std::io::Error> {
1645   #[cfg(target_os = "windows")]
1646   {
1647       let get_lang_options_command = std::process::Command::new("powershell.exe").arg("Get-WinSystemLocale").output();
1648
1649       match get_lang_options_command {
1650           Ok(output) => {
1651               let parse_output = String::from_utf8_lossy(&output.stdout);
1652               
1653               for line in parse_output.lines() {
1654                   if line.starts_with(" ") || line.starts_with("LCID") || line.starts_with("-") || line == "" {
1655                       continue;
1656                   }
1657
1658                   let split_the_line: Vec<&str> = line.split(" ").collect::<Vec<&str>>();
1659
1660                   let mut lcid = "".to_string();
1661                   let mut shortening = "".to_string();
1662                   let mut name = "".to_string();
1663                   let mut country = "".to_string();
1664
1665                   let mut i: u8 = 0;
1666                   for infos in split_the_line {
1667                       if i == 0 {
1668                           i = i + 1;
1669
1670                           lcid = infos.to_string()
1671                       }
1672
1673                       if infos == "" {
1674                           continue;
1675                       }
1676
1677                       if infos.contains("-") {
1678                           shortening = infos.split("-").collect::<Vec<&str>>()[0].to_string()
1679                       } else {
1680                           if infos != "" {
1681                               if infos == "T�rk�e" {
1682                                   country = "Türkiye".to_string();
1683                                   name = "Türkçe".to_string();
1684                               } else {
1685                                   if infos.starts_with("(") {
1686                                       country = infos.replace("(", "").replace("(", "")
1687                                   } else {
1688                                       name = infos.to_string()
1689                                   }
1690                               }
1691                           }
1692                       }
1693
1694                   }
1695
1696                   return Ok(LanguageOptions{
1697                       lcid,
1698                       country,
1699                       name,
1700                       shortening
1701                   })
1702               }
1703
1704               return Ok(LanguageOptions {
1705                   lcid: "".to_string(),
1706                   country: "".to_string(),
1707                   name: "".to_string(),
1708                   shortening: "".to_string()
1709               })
1710           },
1711           Err(error) => {
1712               Err(std::io::Error::new(std::io::ErrorKind::Other, error))
1713           }
1714       }
1715   }
1716
1717   #[cfg(target_os = "linux")]
1718   {
1719       let get_lang_options_command = std::process::Command::new("locale").output();
1720
1721       match get_lang_options_command {
1722           Ok(output) => {
1723               let parse_answer = String::from_utf8_lossy(&output.stdout);
1724
1725               let mut shortening = "".to_string();
1726               let mut character_encoding = "".to_string();
1727               let mut country = "".to_string();
1728           
1729               for line in parse_answer.lines() {
1730                   if line.starts_with("LANG") {
1731                       let split_the_line = line.split("=").collect::<Vec<&str>>()[1];
1732
1733                       let split_the_line_second_time = split_the_line.split(".").collect::<Vec<&str>>();
1734
1735                       character_encoding = split_the_line_second_time[1].to_string();
1736
1737                       let stltt = split_the_line_second_time[0].split("_").collect::<Vec<&str>>();
1738
1739                       shortening = stltt[0].to_string();
1740                       country = stltt[1].to_string();
1741
1742                       break;
1743                   } else {
1744                       continue;
1745                   }
1746               }
1747
1748               return Ok(LanguageOptions {
1749                   shortening,
1750                   country,
1751                   character_encoding
1752               })
1753           },
1754           Err(error) => Err(std::io::Error::new(std::io::ErrorKind::Other, error))
1755       }
1756
1757   }
1758}
1759
1760/// Env level implementation for windows.
1761pub enum EnvLevel {
1762    User, Machine
1763}
1764
1765impl Display for EnvLevel {
1766    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1767        match self {
1768            &EnvLevel::Machine => write!(f, "System"),
1769            &EnvLevel::User => write!(f, "User"),
1770        }
1771    }
1772}
1773
1774/// configurations for working with env's on windows.
1775pub struct EnvOptions {
1776    pub level: EnvLevel,
1777    pub name: String,
1778    pub value: String
1779}
1780
1781/// append a value currently existing env, only windows.
1782pub fn append_env(options: EnvOptions) -> std::result::Result<(), std::io::Error> {
1783    if !cfg!(target_os = "windows") {
1784        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'append_env()' function is only available on windows."));
1785    }
1786
1787    let format_the_command: String;
1788
1789    match options.level {
1790        EnvLevel::User => {
1791            let variable = get_user_env_var(&options.name);
1792
1793            match variable {
1794                Ok(value) => {
1795                    let appended_var = format!("{};{}", value, options.value);
1796
1797                    format_the_command = format!("[System.Environment]::SetEnvironmentVariable('{}', '{}', 'User')", options.name, appended_var);
1798        
1799                },
1800                Err(error) => {
1801                    return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("That error occured when we try to find {} variable in user's variable: {}", options.name, error)))
1802                }
1803            }
1804        },
1805        EnvLevel::Machine => {
1806            let variable = get_system_env_var(&options.name);
1807
1808            match variable {
1809                Ok(value) => {
1810                    let appended_var = format!("{};{}", value, options.value);
1811
1812                    format_the_command = format!("[System.Environment]::SetEnvironmentVariable('{}', '{}', 'Machine')", options.name, appended_var)
1813        
1814                },
1815                Err(error) => {
1816                    return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("That error occured when we try to find {} variable in system's variable: {}", options.name, error)))
1817                }
1818            }
1819        }
1820    }
1821
1822    let execute_appending = std::process::Command::new("powershell.exe")
1823                                                                            .arg("-Command")
1824                                                                            .arg(format_the_command)
1825                                                                            .output();
1826
1827    match execute_appending {
1828        Ok(_) => {
1829            println!("{}'s {} env successfully updated.", options.level, options.name);
1830            Ok(())
1831        },
1832        Err(error) => {
1833            Err(std::io::Error::new(std::io::ErrorKind::Other, format!("That Error Occured When we updating the {}'s {} Env: {}", options.level, options.name, error)))
1834        }
1835    }
1836}
1837
1838
1839/// set an env variable if it's not exist before, only windows.
1840pub fn set_env(options: EnvOptions) -> std::result::Result<(), std::io::Error> {
1841    if !cfg!(target_os = "windows") {
1842        return Err(std::io::Error::new(std::io::ErrorKind::Other, "The 'set_env()' function is only available on windows."));
1843    }
1844
1845    let format_the_command: String;
1846
1847    match options.level {
1848        EnvLevel::User => format_the_command = format!("[System.Environment]::SetEnvironmentVariable('{}', '{}', 'User')", options.name, options.value),
1849        EnvLevel::Machine => format_the_command = format!("[System.Environment]::SetEnvironmentVariable('{}', '{}', 'Machine')", options.name, options.value),
1850    }
1851
1852    let execute_appending = std::process::Command::new("powershell.exe")
1853                                                                        .arg("-Command")
1854                                                                        .arg(format_the_command)
1855                                                                        .output();
1856
1857    match execute_appending {
1858        Ok(_) => {
1859            println!("{}'s {} env successfully updated.", options.level, options.name);
1860            Ok(())
1861        },
1862        Err(error) => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, format!("That Error Occured When we updating the {}'s {} Env: {}", options.level, options.name, error)))
1863    }
1864}
1865
1866/// Type that includes home directory and shell preference of user.
1867#[derive(Debug, Clone)]
1868pub struct UserConfigurations {
1869    pub home_dir: String,
1870    pub shell: String
1871}
1872
1873/// Returns The `UserConfigurations` struct that includes home dir and shell preference of the user. Only works on linux.
1874pub fn get_home_dir_and_shell(username: &str) -> Result<UserConfigurations, std::io::Error> {
1875    if !cfg!(target_os = "linux") {
1876        return Err(std::io::Error::new(std::io::ErrorKind::Other, "'get_home_dir_and_shell()' function is only available on linux."));
1877    }
1878
1879    let path = std::path::Path::new("/etc/passwd");
1880
1881    let file = File::open(&path);
1882
1883    match file {
1884        Ok(file) => {
1885            use std::io::BufRead;
1886
1887            let mut i: usize = 0;
1888
1889            for line in std::io::BufReader::new(file).lines() {
1890                if i == 0 {
1891                    i = 1;
1892                }
1893
1894                match line {
1895                    Ok(line) => {
1896                        match line.starts_with(username) {
1897                            true => {
1898                                let mut split_the_lines = line.split(":");
1899
1900                                match split_the_lines.nth(5) {
1901                                    Some(home_dir) => {
1902                                        match split_the_lines.nth(0) {
1903                                            Some(shell) => {
1904                                                return Ok(UserConfigurations {
1905                                                    home_dir: home_dir.to_string(), 
1906                                                    shell: shell.to_string()
1907                                                })
1908                                            },
1909                                            None => return Err(std::io::Error::new(std::io::ErrorKind::Other, "Error: The /etc/passwd configurations for your user is configured unusually, we cannot find the seventh element on the row of given user."))
1910                                        }
1911                                    },
1912                                    None => return Err(std::io::Error::new(std::io::ErrorKind::Other, "Error: The /etc/passwd configurations for your user is configured unusually, we cannot find the sixth element on the row of given user."))
1913                                }
1914                            }
1915                            false => continue,
1916                        }
1917                    },
1918                    Err(error) => {
1919                        return match error.kind() {
1920                            std::io::ErrorKind::AddrInUse => Err(std::io::Error::new(std::io::ErrorKind::AddrInUse, error)),
1921                            std::io::ErrorKind::AddrNotAvailable => Err(std::io::Error::new(std::io::ErrorKind::AddrNotAvailable, error)),
1922                            std::io::ErrorKind::AlreadyExists => Err(std::io::Error::new(std::io::ErrorKind::AlreadyExists, error)),
1923                            std::io::ErrorKind::BrokenPipe => Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, error)),
1924                            std::io::ErrorKind::ConnectionAborted => Err(std::io::Error::new(std::io::ErrorKind::ConnectionAborted, error)),
1925                            std::io::ErrorKind::ConnectionRefused => Err(std::io::Error::new(std::io::ErrorKind::ConnectionRefused, error)),
1926                            std::io::ErrorKind::ConnectionReset => Err(std::io::Error::new(std::io::ErrorKind::ConnectionReset, error)),
1927                            std::io::ErrorKind::Interrupted => Err(std::io::Error::new(std::io::ErrorKind::Interrupted, error)),
1928                            std::io::ErrorKind::InvalidData => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error)),
1929                            std::io::ErrorKind::InvalidInput => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error)),
1930                            std::io::ErrorKind::NotConnected => Err(std::io::Error::new(std::io::ErrorKind::NotConnected, error)),
1931                            std::io::ErrorKind::NotFound => Err(std::io::Error::new(std::io::ErrorKind::NotFound, error)),
1932                            std::io::ErrorKind::OutOfMemory => Err(std::io::Error::new(std::io::ErrorKind::OutOfMemory, error)),
1933                            std::io::ErrorKind::PermissionDenied => Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, error)),
1934                            std::io::ErrorKind::TimedOut => Err(std::io::Error::new(std::io::ErrorKind::TimedOut, error)),
1935                            std::io::ErrorKind::UnexpectedEof => Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, error)),
1936                            std::io::ErrorKind::Unsupported => Err(std::io::Error::new(std::io::ErrorKind::Unsupported, error)),
1937                            std::io::ErrorKind::WouldBlock => Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, error)),
1938                            std::io::ErrorKind::WriteZero => Err(std::io::Error::new(std::io::ErrorKind::WriteZero, error)),
1939                            _ => Err(std::io::Error::new(std::io::ErrorKind::Other, error)),
1940                        }
1941                    }
1942                }
1943            }
1944
1945            match i {
1946                0 => return Err(std::io::Error::new(std::io::ErrorKind::Other, "An unexpected behavior occured: We could open Your /etc/passwd file but it is empty, which is almost impossible.")),
1947                _ => return Err(std::io::Error::new(std::io::ErrorKind::Other, "It's here because of the rust synthax, impossible to came here."))
1948            }
1949        },
1950        Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error))
1951    };
1952}  
1953
1954/// it returns the system's timezone info. In windows, It returning values are incompatible with tz database timezones such as "Turkey Standard Time" instead of "Europe/Istanbul". 
1955#[cfg(any(target_os = "windows", target_os = "linux"))]
1956pub fn get_timezone() -> Result<String, std::io::Error> {
1957    if !cfg!(target_os = "windows") && !cfg!(target_os = "linux") {
1958        return Err(std::io::Error::new(std::io::ErrorKind::Other, "'get_timezone()' function is only available on linux and windows."));
1959    }
1960
1961    let mut timezone = String::new();
1962
1963    #[cfg(target_os = "windows")]
1964    {
1965    let get_timezone = std::process::Command::new("powershell.exe").arg("Get-TimeZone").output();
1966
1967    match get_timezone {
1968        Ok(tz) => {
1969            let output = String::from_utf8_lossy(&tz.stdout);
1970
1971            for line in output.lines() {
1972                if line.starts_with("Id") {
1973                    timezone = line.split(" : ").nth(1).unwrap().to_string();
1974                }
1975            }
1976        },
1977        Err(error) => return Err(error)
1978    }
1979    }
1980
1981    #[cfg(target_os = "linux")]
1982    {
1983        let path = std::path::Path::new("/etc/timezone");
1984
1985        let file = File::open(&path);
1986
1987        match file {
1988            Ok(file) => {
1989                use std::io::BufRead;
1990
1991                for line in std::io::BufReader::new(file).lines() {
1992                    match line {
1993                        Ok(l) => {
1994                            if !l.starts_with(" ") {
1995                                timezone = l
1996                            }
1997                        },
1998                        Err(error) => {
1999                            return match error.kind() {
2000                                std::io::ErrorKind::AddrInUse => Err(std::io::Error::new(std::io::ErrorKind::AddrInUse, error)),
2001                                std::io::ErrorKind::AddrNotAvailable => Err(std::io::Error::new(std::io::ErrorKind::AddrNotAvailable, error)),
2002                                std::io::ErrorKind::AlreadyExists => Err(std::io::Error::new(std::io::ErrorKind::AlreadyExists, error)),
2003                                std::io::ErrorKind::BrokenPipe => Err(std::io::Error::new(std::io::ErrorKind::BrokenPipe, error)),
2004                                std::io::ErrorKind::ConnectionAborted => Err(std::io::Error::new(std::io::ErrorKind::ConnectionAborted, error)),
2005                                std::io::ErrorKind::ConnectionRefused => Err(std::io::Error::new(std::io::ErrorKind::ConnectionRefused, error)),
2006                                std::io::ErrorKind::ConnectionReset => Err(std::io::Error::new(std::io::ErrorKind::ConnectionReset, error)),
2007                                std::io::ErrorKind::Interrupted => Err(std::io::Error::new(std::io::ErrorKind::Interrupted, error)),
2008                                std::io::ErrorKind::InvalidData => Err(std::io::Error::new(std::io::ErrorKind::InvalidData, error)),
2009                                std::io::ErrorKind::InvalidInput => Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error)),
2010                                std::io::ErrorKind::NotConnected => Err(std::io::Error::new(std::io::ErrorKind::NotConnected, error)),
2011                                std::io::ErrorKind::NotFound => Err(std::io::Error::new(std::io::ErrorKind::NotFound, error)),
2012                                std::io::ErrorKind::OutOfMemory => Err(std::io::Error::new(std::io::ErrorKind::OutOfMemory, error)),
2013                                std::io::ErrorKind::PermissionDenied => Err(std::io::Error::new(std::io::ErrorKind::PermissionDenied, error)),
2014                                std::io::ErrorKind::TimedOut => Err(std::io::Error::new(std::io::ErrorKind::TimedOut, error)),
2015                                std::io::ErrorKind::UnexpectedEof => Err(std::io::Error::new(std::io::ErrorKind::UnexpectedEof, error)),
2016                                std::io::ErrorKind::Unsupported => Err(std::io::Error::new(std::io::ErrorKind::Unsupported, error)),
2017                                std::io::ErrorKind::WouldBlock => Err(std::io::Error::new(std::io::ErrorKind::WouldBlock, error)),
2018                                std::io::ErrorKind::WriteZero => Err(std::io::Error::new(std::io::ErrorKind::WriteZero, error)),
2019                                _ => Err(std::io::Error::new(std::io::ErrorKind::Other, error)),
2020                            }
2021                        }
2022                    }
2023                }
2024
2025            },
2026            Err(error) => return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, error))
2027        }
2028    }
2029
2030    Ok(timezone)
2031}
2032
2033#[cfg(test)]
2034mod test {
2035    use super::*;
2036
2037    #[test]
2038    pub fn test_os_type() {
2039        let typ = os_type().unwrap();
2040        assert!(typ.len() > 0);
2041        println!("os_type(): {}", typ);
2042    }
2043
2044    #[test]
2045    pub fn test_os_release() {
2046        let release = os_release().unwrap();
2047        assert!(release.len() > 0);
2048        println!("os_release(): {}", release);
2049    }
2050
2051    #[test]
2052    pub fn test_cpu_num() {
2053        let num = cpu_num().unwrap();
2054        assert!(num > 0);
2055        println!("cpu_num(): {}", num);
2056    }
2057
2058    #[test]
2059    #[cfg(not(all(target_vendor = "apple", target_arch = "aarch64")))]
2060    pub fn test_cpu_speed() {
2061        let speed = cpu_speed().unwrap();
2062        assert!(speed > 0);
2063        println!("cpu_speed(): {}", speed);
2064    }
2065
2066    #[test]
2067    pub fn test_loadavg() {
2068        let load = loadavg().unwrap();
2069        println!("loadavg(): {:?}", load);
2070    }
2071
2072    #[test]
2073    pub fn test_proc_total() {
2074        let procs = proc_total().unwrap();
2075        assert!(procs > 0);
2076        println!("proc_total(): {}", procs);
2077    }
2078
2079    #[test]
2080    pub fn test_mem_info() {
2081        let mem = mem_info().unwrap();
2082        assert!(mem.total > 0);
2083        println!("mem_info(): {:?}", mem);
2084    }
2085
2086    #[test]
2087    #[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
2088    pub fn test_disk_info() {
2089        let info = disk_info().unwrap();
2090        println!("disk_info(): {:?}", info);
2091    }
2092
2093    #[test]
2094    pub fn test_hostname() {
2095        let host = hostname().unwrap();
2096        assert!(host.len() > 0);
2097        println!("hostname(): {}", host);
2098    }
2099
2100    #[cfg(target_os = "windows")]
2101    #[test]
2102    pub fn test_get_graphics_info(){
2103        let graphics = get_graphics_info();
2104        println!("Graphics info: {:?}", graphics);
2105    }
2106
2107    #[cfg(any(target_os = "linux", target_os = "windows"))]
2108    #[test]
2109    pub fn test_check_computer_type() {
2110        let pc_type = check_computer_type();
2111        println!("computer type: {}", pc_type.unwrap());
2112    }
2113
2114    #[test]
2115    #[cfg(not(windows))]
2116    pub fn test_boottime() {
2117        let bt = boottime().unwrap();
2118        println!("boottime(): {} {}", bt.tv_sec, bt.tv_usec);
2119        assert!(bt.tv_sec > 0 || bt.tv_usec > 0);
2120    }
2121
2122    #[test]
2123    #[cfg(target_os = "linux")]
2124    pub fn test_linux_os_release() {
2125        let os_release = linux_os_release().unwrap();
2126        println!("linux_os_release(): {:?}", os_release.name)
2127    }
2128
2129    #[cfg(any(target_os = "windows", target_os = "linux"))]
2130    #[test]
2131    pub fn test_get_public_ipv4_address(){
2132        assert_ne!(String::new(), get_public_ipv4_address().unwrap())
2133    }
2134
2135    #[cfg(target_os = "windows")]
2136    #[test]
2137    pub fn test_is_program_installed_search_hard(){
2138        let hard_search_options = HardSearchOptions {
2139            case_sensitive: false, // if this is false, you don't need to match lower or upper cases.
2140            search_hardness: 5 // the biggest level is 6, and it's it's slowest level. If your program is available on terminal, choose 3 instead.
2141        };
2142
2143        assert_eq!(true, is_program_installed_search_hard("miCroSoft eDgE", hard_search_options).unwrap());
2144
2145        let hard_search_options_2 = HardSearchOptions {
2146            case_sensitive: true,
2147            search_hardness: 5
2148        };
2149
2150        assert_eq!(true, is_program_installed_search_hard("Microsoft Edge", hard_search_options_2).unwrap())
2151    }
2152
2153    #[cfg(target_os = "windows")]
2154    #[test]
2155    pub fn test_get_ram_infos() {
2156        let ram_infos = get_ram_infos().unwrap();
2157        assert!(ram_infos.len() > 0);
2158        println!("get_ram_infos(): {:#?}", ram_infos);
2159    }
2160
2161    #[cfg(any(target_os = "windows", target_os = "linux"))]
2162    #[test]
2163    pub fn test_get_system_env_var(){
2164        let path_env_var = get_system_env_var("PATH");
2165
2166        assert_eq!(true, path_env_var.is_ok());
2167    }
2168
2169    #[cfg(target_os = "windows")]
2170    #[test]
2171    pub fn test_get_user_env_var(){
2172        let path_env_var = get_user_env_var("PATH");
2173
2174        assert_eq!(true, path_env_var.is_ok());
2175    }
2176
2177    #[cfg(any(target_os = "windows", target_os = "linux"))]
2178    #[test]
2179    pub fn test_get_timezone(){
2180        let timezone = get_timezone();
2181
2182        assert_eq!(true, timezone.is_ok())
2183    }
2184}