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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! Process environment management.
use crate::{
    alloc::NSTD_ALLOCATOR,
    core::{
        alloc::NSTDAllocError::NSTD_ALLOC_ERROR_NONE, optional::NSTDOptional, result::NSTDResult,
        str::NSTDStr,
    },
    io::{NSTDIOError, NSTDIOStringResult},
    string::{NSTDOptionalString, NSTDString},
    vec::{nstd_vec_new, nstd_vec_push, NSTDVec},
};
use core::ptr::addr_of;
use nstdapi::nstdapi;
use std::env::VarError;

/// Returns a complete path to the process's current working directory.
///
/// # Returns
///
/// `NSTDIOStringResult working_dir` - A path to the current working directory on success, or the
/// I/O operation error code on failure.
#[nstdapi]
pub fn nstd_env_current_dir() -> NSTDIOStringResult<'static> {
    match std::env::current_dir() {
        Ok(dir) => dir.to_str().map_or(
            NSTDResult::Err(NSTDIOError::NSTD_IO_ERROR_INVALID_DATA),
            |dir| NSTDResult::Ok(NSTDString::from_string(dir.into())),
        ),
        Err(err) => NSTDResult::Err(NSTDIOError::from_err(err.kind())),
    }
}

/// Returns a complete path to the process executable.
///
/// # Note
///
/// Please see Rust's documentation for information about the security of this function
/// <https://doc.rust-lang.org/std/env/fn.current_exe.html>.
///
/// # Returns
///
/// `NSTDIOStringResult exe` - A complete path to process executable on success, or the I/O
/// operation error code on failure.
#[nstdapi]
pub fn nstd_env_current_exe() -> NSTDIOStringResult<'static> {
    match std::env::current_exe() {
        Ok(exe) => exe.to_str().map_or(
            NSTDResult::Err(NSTDIOError::NSTD_IO_ERROR_INVALID_DATA),
            |exe| NSTDResult::Ok(NSTDString::from_string(exe.into())),
        ),
        Err(err) => NSTDResult::Err(NSTDIOError::from_err(err.kind())),
    }
}

/// Returns a complete path to a temporary directory.
///
/// # Returns
///
/// `NSTDOptionalString temp` - A path to the temporary directory.
#[nstdapi]
pub fn nstd_env_temp_dir() -> NSTDOptionalString<'static> {
    std::env::temp_dir()
        .to_str()
        .map_or(NSTDOptional::None, |temp| {
            NSTDOptional::Some(NSTDString::from_string(temp.into()))
        })
}

/// Sets the current working directory for the process.
///
/// # Parameters:
///
/// - `const NSTDStr *path` - The directory to set as the process working directory.
///
/// # Returns
///
/// `NSTDIOError errc` - The I/O operation error code.
///
/// # Safety
///
/// The user of this function must ensure that `path` is valid for reads.
#[nstdapi]
pub unsafe fn nstd_env_set_current_dir(path: &NSTDStr) -> NSTDIOError {
    match std::env::set_current_dir(path.as_str()) {
        Err(err) => NSTDIOError::from_err(err.kind()),
        _ => NSTDIOError::NSTD_IO_ERROR_NONE,
    }
}

/// Retrieves a variable from the process environment.
///
/// # Parameters:
///
/// - `const NSTDStr *key` - The variable's key.
///
/// # Returns
///
/// `NSTDIOStringResult var` - The value of the environment variable, or the I/O operation error
/// code on failure. This will return as `NSTD_IO_ERROR_NOT_FOUND` if they variable cannot be found,
/// and `NSTD_IO_ERROR_INVALID_DATA` if the variable isn't valid Unicode.
///
/// # Safety
///
/// The user of this function must ensure that `key` is valid for reads.
#[nstdapi]
pub unsafe fn nstd_env_var(key: &NSTDStr) -> NSTDIOStringResult<'_> {
    match std::env::var(key.as_str()) {
        Ok(var) => NSTDResult::Ok(NSTDString::from_string(var)),
        Err(VarError::NotPresent) => NSTDResult::Err(NSTDIOError::NSTD_IO_ERROR_NOT_FOUND),
        Err(VarError::NotUnicode(_)) => NSTDResult::Err(NSTDIOError::NSTD_IO_ERROR_INVALID_DATA),
    }
}

/// Sets an environment variable for the current process.
///
/// # Parameters:
///
/// - `const NSTDStr *key` - The environment variable's identification key.
///
/// - `const NSTDStr *value` - The environment variable's value.
///
/// # Panics
///
/// This operation will panic in the following situations:
///
/// - `key` is empty or contains either of the following ASCII characters: `'='` or `'\0'`.
///
/// - `value` contains the ASCII null character `'\0'`.
///
/// # Safety
///
/// The user of this function must ensure that both `key` and `value` are valid for reads.
#[inline]
#[nstdapi]
pub unsafe fn nstd_env_set_var(key: &NSTDStr, value: &NSTDStr) {
    std::env::set_var(key.as_str(), value.as_str());
}

/// Removes an environment variable from the current process.
///
/// # Parameters:
///
/// - `const NSTDStr *key` - The environment variable's identification key.
///
/// # Panics
///
/// This operation will panic in the following situations:
///
/// - `key` is empty or contains either of the following ASCII characters: `'='` or `'\0'`.
///
/// - The environment variable's value contains the ASCII null character `'\0'`.
///
/// # Safety
///
/// The user of this function must ensure that `key` is valid for reads.
#[inline]
#[nstdapi]
pub unsafe fn nstd_env_remove_var(key: &NSTDStr) {
    std::env::remove_var(key.as_str());
}

/// Returns an `NSTDVec` of `NSTDString`s that each represent an argument received at program start.
///
/// # Returns
///
/// `NSTDVec args` - The `NSTDString` arguments that the program was started with.
///
/// # Panics
///
/// This operation will panic if any program arguments contain invalid Unicode.
#[nstdapi]
pub fn nstd_env_args() -> NSTDVec<'static> {
    let size = core::mem::size_of::<NSTDString<'_>>();
    let align = core::mem::align_of::<NSTDString<'_>>();
    let mut args = nstd_vec_new(&NSTD_ALLOCATOR, size, align);
    for arg in std::env::args() {
        let arg = NSTDString::from_string(arg);
        // SAFETY: `arg` is stored on the stack.
        let errc = unsafe { nstd_vec_push(&mut args, addr_of!(arg).cast()) };
        if errc == NSTD_ALLOC_ERROR_NONE {
            core::mem::forget(arg);
        }
    }
    args
}

/// Returns an `NSTDVec` of `NSTDString[2]` which each represent an environment variable from the
/// current process.
///
/// # Returns
///
/// `NSTDVec vars` - A list of the process environment variables.
///
/// # Panics
///
/// This operation will panic if any environment variables contain invalid Unicode.
#[nstdapi]
pub fn nstd_env_vars() -> NSTDVec<'static> {
    let size = core::mem::size_of::<[NSTDString<'_>; 2]>();
    let align = core::mem::align_of::<[NSTDString<'_>; 2]>();
    let mut vars = nstd_vec_new(&NSTD_ALLOCATOR, size, align);
    for (k, v) in std::env::vars() {
        let var = [NSTDString::from_string(k), NSTDString::from_string(v)];
        // SAFETY: `var` is stored on the stack.
        let errc = unsafe { nstd_vec_push(&mut vars, addr_of!(var).cast()) };
        if errc == NSTD_ALLOC_ERROR_NONE {
            core::mem::forget(var);
        }
    }
    vars
}