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
//! Calling/Child process management.
use crate::{
    alloc::CBox,
    core::{
        optional::{gen_optional, NSTDOptional},
        slice::NSTDSlice,
        str::NSTDStr,
    },
    io::NSTDIOError,
    NSTDInt32, NSTDUInt32,
};
use nstdapi::nstdapi;
use std::process::{Child, Command};

/// A handle to a child process.
#[nstdapi]
pub struct NSTDChildProcess {
    /// A handle to a child process.
    proc: CBox<Child>,
}
gen_optional!(NSTDOptionalChildProcess, NSTDChildProcess);

/// Spawns a new child process with the name `program` and returns a handle to it.
///
/// # Parameters:
///
/// - `const NSTDStr *program` - A path to the program to run as a child process.
///
/// - `const NSTDSlice *args` - A slice of `NSTDStr` arguments to pass to the program.
///
/// - `const NSTDSlice *vars` - A slice of `NSTDStr[2]` key/value environment variables to
/// give to the program.
///
/// # Returns
///
/// `NSTDOptionalChildProcess child` - A handle to the new child process on success, or an
/// uninitialized "none" variant if spawning the child process fails.
///
/// # Safety
///
/// The user must ensure that all of `program`, `args`, and `vars` and their data remain valid for
/// reads while this function is executing.
#[nstdapi]
pub unsafe fn nstd_proc_spawn(
    program: &NSTDStr,
    args: &NSTDSlice,
    vars: &NSTDSlice,
) -> NSTDOptionalChildProcess {
    // Create the process command builder.
    let mut cmd = Command::new(program.as_str());
    if let Some(args) = args.as_slice::<NSTDStr>() {
        if let Some(vars) = vars.as_slice::<[NSTDStr; 2]>() {
            // Add the arguments.
            cmd.args(args.iter().map(|arg| arg.as_str()));
            // Add the environment variables.
            cmd.envs(vars.iter().map(|vars| {
                (
                    vars.get_unchecked(0).as_str(),
                    vars.get_unchecked(1).as_str(),
                )
            }));
            // Spawn the process.
            if let Ok(proc) = cmd.spawn() {
                if let Some(proc) = CBox::new(proc) {
                    return NSTDOptional::Some(NSTDChildProcess { proc });
                }
            }
        }
    }
    NSTDOptional::None
}

/// Returns the OS-assigned ID of a child process.
///
/// # Parameters:
///
/// - `const NSTDChildProcess *handle` - A handle to the process.
///
/// # Returns
///
/// `NSTDUInt32 ID` - The child process ID.
#[inline]
#[nstdapi]
pub fn nstd_proc_child_id(handle: &NSTDChildProcess) -> NSTDUInt32 {
    handle.proc.id()
}

/// Attempts to kill a child process.
///
/// # Parameters:
///
/// - `NSTDChildProcess *handle` - A handle to the child process.
///
/// # Returns
///
/// `NSTDIOError errc` - The operation error code.
#[inline]
#[nstdapi]
pub fn nstd_proc_kill(handle: &mut NSTDChildProcess) -> NSTDIOError {
    if let Err(err) = handle.proc.kill() {
        return NSTDIOError::from_err(err.kind());
    }
    NSTDIOError::NSTD_IO_ERROR_NONE
}

/// Waits for a child process to exit.
///
/// # Parameters:
///
/// - `NSTDChildProcess *handle` - A handle to the process.
///
/// # Returns
///
/// `NSTDIOError errc` - The operation error code.
#[nstdapi]
pub fn nstd_proc_join(handle: &mut NSTDChildProcess) -> NSTDIOError {
    match handle.proc.wait() {
        Ok(status) if status.success() => NSTDIOError::NSTD_IO_ERROR_NONE,
        Err(err) => NSTDIOError::from_err(err.kind()),
        _ => NSTDIOError::NSTD_IO_ERROR_UNKNOWN,
    }
}

/// Frees a handle to a child process, allowing the process to run in the background.
///
/// # Parameters:
///
/// - `NSTDChildProcess handle` - A handle to the child process.
#[inline]
#[nstdapi]
#[allow(
    unused_variables,
    clippy::missing_const_for_fn,
    clippy::needless_pass_by_value
)]
pub fn nstd_proc_free(handle: NSTDChildProcess) {}

/// Terminates the process with the given `exit_code`.
///
/// # Parameters:
///
/// - `NSTDInt32 exit_code` - The process exit code.
///
/// # Example
///
/// ```
/// use nstd_sys::proc::nstd_proc_exit;
///
/// unsafe { nstd_proc_exit(0) };
/// ```
#[inline]
#[nstdapi]
pub fn nstd_proc_exit(exit_code: NSTDInt32) -> ! {
    std::process::exit(exit_code);
}

/// Terminates the program in an abnormal fashion.
#[inline]
#[nstdapi]
pub fn nstd_proc_abort() -> ! {
    std::process::abort();
}

/// Returns the ID of the current process.
///
/// # Returns
///
/// `NSTDUInt32 ID` - The process ID.
#[inline]
#[nstdapi]
pub fn nstd_proc_id() -> NSTDUInt32 {
    std::process::id()
}