whoami/
api.rs

1use std::ffi::OsString;
2
3use crate::{
4    fallible,
5    os::{Os, Target},
6    Arch, DesktopEnv, Language, Platform, Result,
7};
8
9macro_rules! report_message {
10    () => {
11        "Please report this issue at https://github.com/ardaku/whoami/issues"
12    };
13}
14
15const DEFAULT_USERNAME: &str = "Unknown";
16const DEFAULT_HOSTNAME: &str = "LocalHost";
17
18/// Get the CPU Architecture.
19#[inline(always)]
20pub fn arch() -> Arch {
21    Target::arch(Os).expect(concat!("arch() failed.  ", report_message!()))
22}
23
24/// Get the user's username.
25///
26/// On unix-systems this differs from [`realname()`] most notably in that spaces
27/// are not allowed in the username.
28#[inline(always)]
29pub fn username() -> String {
30    fallible::username().unwrap_or_else(|_| DEFAULT_USERNAME.to_lowercase())
31}
32
33/// Get the user's username.
34///
35/// On unix-systems this differs from [`realname_os()`] most notably in that
36/// spaces are not allowed in the username.
37#[inline(always)]
38pub fn username_os() -> OsString {
39    fallible::username_os()
40        .unwrap_or_else(|_| DEFAULT_USERNAME.to_lowercase().into())
41}
42
43/// Get the user's real (full) name.
44#[inline(always)]
45pub fn realname() -> String {
46    fallible::realname()
47        .or_else(|_| fallible::username())
48        .unwrap_or_else(|_| DEFAULT_USERNAME.to_owned())
49}
50
51/// Get the user's real (full) name.
52#[inline(always)]
53pub fn realname_os() -> OsString {
54    fallible::realname_os()
55        .or_else(|_| fallible::username_os())
56        .unwrap_or_else(|_| DEFAULT_USERNAME.to_owned().into())
57}
58
59/// Get the device name (also known as "Pretty Name").
60///
61/// Often used to identify device for bluetooth pairing.
62#[inline(always)]
63pub fn devicename() -> String {
64    fallible::devicename()
65        .or_else(|_| fallible::hostname())
66        .unwrap_or_else(|_| DEFAULT_HOSTNAME.to_string())
67}
68
69/// Get the device name (also known as "Pretty Name").
70///
71/// Often used to identify device for bluetooth pairing.
72#[inline(always)]
73pub fn devicename_os() -> OsString {
74    fallible::devicename_os()
75        .or_else(|_| fallible::hostname().map(OsString::from))
76        .unwrap_or_else(|_| DEFAULT_HOSTNAME.to_string().into())
77}
78
79/// Get the host device's hostname.
80///
81/// This method normalizes to lowercase.  Usually hostnames are
82/// case-insensitive, but it's not a hard requirement.
83///
84/// FIXME: Document platform-specific character limitations
85///
86/// Use [`fallible::hostname()`] for case-sensitive hostname.
87#[inline(always)]
88#[deprecated(note = "use `fallible::hostname()` instead", since = "1.5.0")]
89pub fn hostname() -> String {
90    let mut hostname = fallible::hostname()
91        .unwrap_or_else(|_| DEFAULT_HOSTNAME.to_lowercase());
92
93    hostname.make_ascii_lowercase();
94    hostname
95}
96
97/// Get the host device's hostname.
98///
99/// Usually hostnames are case-insensitive, but it's
100/// not a hard requirement.
101///
102/// Use [`fallible::hostname()`] for case-sensitive hostname.
103#[inline(always)]
104#[deprecated(note = "use `fallible::hostname()` instead", since = "1.5.0")]
105pub fn hostname_os() -> OsString {
106    #[allow(deprecated)]
107    hostname().into()
108}
109
110/// Get the name of the operating system distribution and (possibly) version.
111///
112/// Example: "Windows 10" or "Fedora 26 (Workstation Edition)"
113#[inline(always)]
114pub fn distro() -> String {
115    fallible::distro().unwrap_or_else(|_| format!("Unknown {}", platform()))
116}
117
118/// Get the name of the operating system distribution and (possibly) version.
119///
120/// Example: "Windows 10" or "Fedora 26 (Workstation Edition)"
121#[inline(always)]
122#[deprecated(note = "use `distro()` instead", since = "1.5.0")]
123pub fn distro_os() -> OsString {
124    fallible::distro()
125        .map(OsString::from)
126        .unwrap_or_else(|_| format!("Unknown {}", platform()).into())
127}
128
129/// Get the desktop environment.
130///
131/// Example: "gnome" or "windows"
132#[inline(always)]
133pub fn desktop_env() -> DesktopEnv {
134    Target::desktop_env(Os)
135}
136
137/// Get the platform.
138#[inline(always)]
139pub fn platform() -> Platform {
140    Target::platform(Os)
141}
142
143/// Get the user's preferred language(s).
144///
145/// Returned as iterator of two letter language codes (lowercase), optionally
146/// followed by a dash (-) and a two letter country code (uppercase).  The most
147/// preferred language is returned first, followed by next preferred, and so on.
148#[inline(always)]
149#[deprecated(note = "use `langs()` instead", since = "1.5.0")]
150pub fn lang() -> impl Iterator<Item = String> {
151    let langs_vec = if let Ok(langs) = langs() {
152        langs
153            .map(|lang| lang.to_string().replace('/', "-"))
154            .collect()
155    } else {
156        ["en-US".to_string()].to_vec()
157    };
158
159    langs_vec.into_iter()
160}
161
162/// Get the user's preferred language(s).
163///
164/// Returned as iterator of [`Language`]s.  The most preferred language is
165/// returned first, followed by next preferred, and so on.  Unrecognized
166/// languages may either return an error or be skipped.
167#[inline(always)]
168pub fn langs() -> Result<impl Iterator<Item = Language>> {
169    // FIXME: Could do less allocation
170    let langs = Target::langs(Os)?;
171    let langs = langs
172        .split(':')
173        .map(ToString::to_string)
174        .filter_map(|lang| {
175            let lang = lang
176                .split_terminator('.')
177                .next()
178                .unwrap_or_default()
179                .replace(|x| ['_', '-'].contains(&x), "/");
180
181            if lang == "C" {
182                return None;
183            }
184
185            Some(Language::__(Box::new(lang)))
186        });
187
188    Ok(langs.collect::<Vec<_>>().into_iter())
189}