whoami/
os.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#![allow(unsafe_code)]

// Daku
#[cfg_attr(all(target_arch = "wasm32", daku), path = "os/daku.rs")]
// Redox
#[cfg_attr(
    all(target_os = "redox", not(target_arch = "wasm32")),
    path = "os/redox.rs"
)]
// Unix
#[cfg_attr(
    all(
        any(
            target_os = "linux",
            target_os = "macos",
            target_os = "dragonfly",
            target_os = "freebsd",
            target_os = "netbsd",
            target_os = "openbsd",
            target_os = "illumos",
            target_os = "hurd",
        ),
        not(target_arch = "wasm32")
    ),
    path = "os/unix.rs"
)]
// Wasi
#[cfg_attr(
    all(target_arch = "wasm32", target_os = "wasi"),
    path = "os/wasi.rs"
)]
// Web
#[cfg_attr(
    all(
        target_arch = "wasm32",
        not(target_os = "wasi"),
        not(daku),
        feature = "web",
    ),
    path = "os/web.rs"
)]
// Windows
#[cfg_attr(
    all(target_os = "windows", not(target_arch = "wasm32")),
    path = "os/windows.rs"
)]
mod target;

use std::{
    env::{self, VarError},
    ffi::OsString,
    io::{Error, ErrorKind},
};

use crate::{Arch, DesktopEnv, Platform, Result};

/// Implement `Target for Os` to add platform support for a target.
pub(crate) struct Os;

/// Target platform support
pub(crate) trait Target: Sized {
    /// Return a semicolon-delimited string of language/COUNTRY codes.
    fn langs(self) -> Result<String>;
    /// Return the user's "real" / "full" name.
    fn realname(self) -> Result<OsString>;
    /// Return the user's username.
    fn username(self) -> Result<OsString>;
    /// Return the computer's "fancy" / "pretty" name.
    fn devicename(self) -> Result<OsString>;
    /// Return the computer's hostname.
    fn hostname(self) -> Result<String>;
    /// Return the OS distribution's name.
    fn distro(self) -> Result<String>;
    /// Return the desktop environment.
    fn desktop_env(self) -> DesktopEnv;
    /// Return the target platform.
    fn platform(self) -> Platform;
    /// Return the computer's CPU architecture.
    fn arch(self) -> Result<Arch>;

    /// Return the user's account name (usually just the username, but may
    /// include an account server hostname).
    fn account(self) -> Result<OsString> {
        self.username()
    }
}

// This is only used on some platforms
#[allow(dead_code)]
fn err_missing_record() -> Error {
    Error::new(ErrorKind::NotFound, "Missing record")
}

// This is only used on some platforms
#[allow(dead_code)]
fn err_null_record() -> Error {
    Error::new(ErrorKind::NotFound, "Null record")
}

// This is only used on some platforms
#[allow(dead_code)]
fn err_empty_record() -> Error {
    Error::new(ErrorKind::NotFound, "Empty record")
}

// This is only used on some platforms
#[allow(dead_code)]
fn unix_lang() -> Result<String> {
    let check_var = |var| {
        env::var(var).map_err(|e| {
            let kind = match e {
                VarError::NotPresent => ErrorKind::NotFound,
                VarError::NotUnicode(_) => ErrorKind::InvalidData,
            };
            Error::new(kind, e)
        })
    };

    // Uses priority defined in
    // <https://www.gnu.org/software/gettext/manual/html_node/Locale-Environment-Variables.html>
    let locale = check_var("LC_ALL").or_else(|_| check_var("LANG"));

    // The LANGUAGE environment variable takes precedence if and only if
    // localization is enabled, i.e., LC_ALL / LANG is not "C".
    // <https://www.gnu.org/software/gettext/manual/html_node/The-LANGUAGE-variable.html>
    let langs = match &locale {
        Ok(loc) if loc != "C" => check_var("LANGUAGE").or(locale),
        _ => locale,
    }?;

    if langs.is_empty() {
        return Err(err_empty_record());
    }

    Ok(langs)
}