Skip to main content

vyder_std/
env.rs

1//! Various functions to interact with the environment.
2//!
3//! Available under the name `std-env`.
4
5use std::process::ExitCode;
6
7use vyder::{values, Error, ExpectArity, Module, Span, Value, ValueResult};
8
9/// Execute a shell command and waits for it to finish.
10///
11/// The first argument is the command the execute, the following arguments are given as arguments to the command.
12/// Returns a map containing `stdout`, `stderr` and `exit_code`.
13pub fn exec(
14    arguments: &[ValueResult],
15    span: Span,
16) -> Result<(ValueResult, Option<ExitCode>), Error> {
17    let (process, string_arguments) = get_command_arguments(arguments, span.clone())?;
18
19    let output = match std::process::Command::new(process)
20        .args(string_arguments)
21        .output()
22    {
23        Ok(child) => child,
24        Err(_) => {
25            return Ok((
26                Value::new_err_value(
27                    values::Str {
28                        value: "failed to spawn child process".to_string(),
29                    }
30                    .into(),
31                    span.clone(),
32                ),
33                None,
34            ));
35        }
36    };
37
38    let stdout = String::from_utf8_lossy(&output.stdout).into_owned();
39    let stderr = String::from_utf8_lossy(&output.stderr).into_owned();
40    let exit_code = output.status.code().unwrap_or(0) as f64;
41
42    Ok((
43        Value::new_ok_value(
44            values::Map {
45                values: vec![
46                    (
47                        values::Str {
48                            value: "stdout".to_string(),
49                        }
50                        .into(),
51                        values::Str { value: stdout }.into(),
52                    ),
53                    (
54                        values::Str {
55                            value: "stderr".to_string(),
56                        }
57                        .into(),
58                        values::Str { value: stderr }.into(),
59                    ),
60                    (
61                        values::Str {
62                            value: "exit_code".to_string(),
63                        }
64                        .into(),
65                        values::Number { value: exit_code }.into(),
66                    ),
67                ],
68            }
69            .into(),
70            span,
71        ),
72        None,
73    ))
74}
75
76/// Execute a shell command and doesn't wait for it to finish.
77///
78/// The first argument is the command the execute, the following arguments are given as arguments to the command.
79pub fn spawn(
80    arguments: &[ValueResult],
81    span: Span,
82) -> Result<(ValueResult, Option<ExitCode>), Error> {
83    let (process, string_arguments) = get_command_arguments(arguments, span.clone())?;
84
85    let _child = match std::process::Command::new(process)
86        .args(string_arguments)
87        .spawn()
88    {
89        Ok(child) => child,
90        Err(_) => {
91            return Ok((
92                Value::new_err_value(
93                    values::Str {
94                        value: "failed to spawn child process".to_string(),
95                    }
96                    .into(),
97                    span.clone(),
98                ),
99                None,
100            ));
101        }
102    };
103
104    Ok((Value::new_ok_value(values::Nil.into(), span), None))
105}
106
107fn get_command_arguments(
108    arguments: &[ValueResult],
109    span: Span,
110) -> Result<(String, Vec<String>), Error> {
111    let (process, arguments) = arguments.to_vec().expect_min::<1>(&span)?;
112    let process = process[0]
113        .clone()
114        .expect_non_error()?
115        .expect::<values::Str>()?
116        .value;
117
118    let mut string_arguments = vec![];
119    for argument in arguments {
120        string_arguments.push(argument.expect_non_error()?.expect::<values::Str>()?.value);
121    }
122
123    Ok((process, string_arguments))
124}
125
126pub fn get_module() -> Module {
127    Module::builder()
128        .function("exec", exec, true)
129        .function("spawn", spawn, true)
130        .build()
131}