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
#![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",
        ),
        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)
        })
    };
    let langs = check_var("LANGS").or_else(|_| check_var("LANG"))?;

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

    Ok(langs)
}