whoami/
os.rs

1#![allow(unsafe_code)]
2
3// Daku
4#[cfg_attr(
5    all(
6        not(any(
7            feature = "force-stub",
8            all(target_os = "wasi", feature = "wasi-wasite")
9        )),
10        target_arch = "wasm32",
11        daku,
12    ),
13    path = "os/daku.rs"
14)]
15// Redox
16#[cfg_attr(
17    all(
18        not(any(feature = "force-stub", target_arch = "wasm32")),
19        feature = "std",
20        target_os = "redox",
21    ),
22    path = "os/redox.rs"
23)]
24// Unix
25#[cfg_attr(
26    all(
27        not(any(feature = "force-stub", target_arch = "wasm32")),
28        feature = "std",
29        any(
30            target_vendor = "apple",
31            target_os = "linux",
32            target_os = "dragonfly",
33            target_os = "freebsd",
34            target_os = "netbsd",
35            target_os = "openbsd",
36            target_os = "illumos",
37            target_os = "hurd",
38        ),
39    ),
40    path = "os/unix.rs"
41)]
42// Wasite WASM
43#[cfg_attr(
44    all(
45        not(feature = "force-stub"),
46        target_arch = "wasm32",
47        target_os = "wasi",
48        feature = "wasi-wasite",
49    ),
50    path = "os/wasite.rs"
51)]
52// Web WASM
53#[cfg_attr(
54    all(
55        not(any(
56            feature = "force-stub",
57            daku,
58            all(target_os = "wasi", feature = "wasi-wasite")
59        )),
60        target_arch = "wasm32",
61        feature = "wasm-web",
62    ),
63    path = "os/web.rs"
64)]
65// Windows
66#[cfg_attr(
67    all(
68        not(any(feature = "force-stub", target_arch = "wasm32")),
69        feature = "std",
70        target_os = "windows",
71    ),
72    path = "os/windows.rs"
73)]
74mod stub;
75
76use alloc::string::String;
77
78use crate::{
79    CpuArchitecture, DesktopEnvironment, LanguagePreferences, OsString,
80    Platform, Result,
81};
82
83/// Implement `Target for Os` to add platform support for a target.
84pub(crate) struct Os;
85
86/// Target platform support
87pub(crate) trait Target: Sized {
88    /// Return a semicolon-delimited string of language/COUNTRY codes.
89    fn lang_prefs(self) -> Result<LanguagePreferences>;
90    /// Return the user's "real" / "full" name.
91    fn realname(self) -> Result<OsString>;
92    /// Return the user's username.
93    fn username(self) -> Result<OsString>;
94    /// Return the computer's "fancy" / "pretty" name.
95    fn devicename(self) -> Result<OsString>;
96    /// Return the computer's hostname.
97    fn hostname(self) -> Result<String>;
98    /// Return the OS distribution's name.
99    fn distro(self) -> Result<String>;
100    /// Return the desktop environment.
101    fn desktop_env(self) -> Option<DesktopEnvironment>;
102    /// Return the target platform.
103    fn platform(self) -> Platform;
104    /// Return the computer's CPU architecture.
105    fn arch(self) -> Result<CpuArchitecture>;
106
107    /// Return the user's account name (usually just the username, but may
108    /// include an account server hostname).
109    fn account(self) -> Result<OsString> {
110        self.username()
111    }
112}
113
114// This is only used on some platforms
115#[cfg(feature = "std")]
116#[allow(dead_code)]
117fn unix_lang() -> Result<LanguagePreferences> {
118    use std::{
119        env::{self, VarError},
120        str::FromStr,
121        vec::Vec,
122    };
123
124    use crate::{Error, Language};
125
126    let env_var = |var: &str| match env::var(var) {
127        Ok(value) => Ok(if value.is_empty() { None } else { Some(value) }),
128        Err(VarError::NotPresent) => Ok(None),
129        Err(VarError::NotUnicode(_)) => {
130            Err(Error::with_invalid_data("not unicode"))
131        }
132    };
133
134    // Uses priority defined in
135    // <https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>
136    let lc_all = env_var("LC_ALL")?;
137    let lang = env_var("LANG")?;
138
139    if lang.is_none() && lc_all.is_none() {
140        return Err(Error::empty_record());
141    }
142
143    // Standard locales that have a higher global precedence than their specific
144    // counterparts, indicating that one should not perform any localization.
145    // https://www.gnu.org/software/libc/manual/html_node/Standard-Locales.html
146    if let Some(l) = &lang {
147        if l == "C" || l == "POSIX" {
148            return Ok(LanguagePreferences {
149                fallbacks: Vec::new(),
150                ..Default::default()
151            });
152        }
153    }
154
155    // The LANGUAGE environment variable takes precedence if and only if
156    // localization is enabled, i.e., LC_ALL / LANG is not "C" or "POSIX".
157    // <https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html>
158    if let Some(language) = env_var("LANGUAGE")? {
159        return Ok(LanguagePreferences {
160            fallbacks: language
161                .split(":")
162                .map(Language::from_str)
163                .collect::<Result<_>>()?,
164            ..Default::default()
165        });
166    }
167
168    // All fields other than LANGUAGE can only contain a single value, so we
169    // don't need to perform any splitting at this point.
170    let lang_from_var = |var| -> Result<Option<Language>> {
171        env_var(var)?.as_deref().map(Language::from_str).transpose()
172    };
173
174    Ok(LanguagePreferences {
175        fallbacks: lang
176            .as_ref()
177            .map(|l| -> Result<_> { Ok([Language::from_str(l)?].to_vec()) })
178            .transpose()?
179            .unwrap_or(Vec::new()),
180        collation: lang_from_var("LC_COLLATE")?,
181        char_classes: lang_from_var("LC_CTYPE")?,
182        monetary: lang_from_var("LC_MONETARY")?,
183        messages: lang_from_var("LC_MESSAGES")?,
184        numeric: lang_from_var("LC_NUMERIC")?,
185        time: lang_from_var("LC_TIME")?,
186    })
187}