nu_test_support/playground/
director.rs

1use super::EnvironmentVariable;
2use super::nu_process::*;
3use std::ffi::OsString;
4use std::fmt;
5use std::fmt::Write;
6
7#[derive(Default, Debug)]
8pub struct Director {
9    pub cwd: Option<OsString>,
10    pub environment_vars: Vec<EnvironmentVariable>,
11    pub config: Option<OsString>,
12    pub pipeline: Option<Vec<String>>,
13    pub executable: Option<NuProcess>,
14}
15
16impl Director {
17    pub fn cococo(&self, arg: &str) -> Self {
18        let mut process = NuProcess {
19            environment_vars: self.environment_vars.clone(),
20            ..Default::default()
21        };
22
23        process.args(&["--testbin", "cococo", arg]);
24        Director {
25            config: self.config.clone(),
26            executable: Some(process),
27            environment_vars: self.environment_vars.clone(),
28            ..Default::default()
29        }
30    }
31
32    pub fn and_then(&mut self, commands: &str) -> &mut Self {
33        let commands = commands.to_string();
34
35        if let Some(ref mut pipeline) = self.pipeline {
36            pipeline.push(commands);
37        } else {
38            self.pipeline = Some(vec![commands]);
39        }
40
41        self
42    }
43
44    pub fn pipeline(&self, commands: &str) -> Self {
45        let mut director = Director {
46            pipeline: if commands.is_empty() {
47                None
48            } else {
49                Some(vec![commands.to_string()])
50            },
51            ..Default::default()
52        };
53
54        let mut process = NuProcess {
55            environment_vars: self.environment_vars.clone(),
56            ..Default::default()
57        };
58
59        if let Some(working_directory) = &self.cwd {
60            process.cwd(working_directory);
61        }
62
63        process.arg("--no-history");
64        if let Some(config_file) = self.config.as_ref() {
65            process.args(&[
66                "--config",
67                config_file.to_str().expect("failed to convert."),
68            ]);
69        }
70        process.args(&["--log-level", "info"]);
71
72        director.executable = Some(process);
73        director
74    }
75
76    pub fn executable(&self) -> Option<&NuProcess> {
77        if let Some(binary) = &self.executable {
78            Some(binary)
79        } else {
80            None
81        }
82    }
83}
84
85impl Executable for Director {
86    fn execute(&mut self) -> Result<Outcome, NuError> {
87        use std::process::Stdio;
88
89        match self.executable() {
90            Some(binary) => {
91                let mut commands = String::new();
92                if let Some(pipelines) = &self.pipeline {
93                    for pipeline in pipelines {
94                        if !commands.is_empty() {
95                            commands.push_str("| ");
96                        }
97                        let _ = writeln!(commands, "{pipeline}");
98                    }
99                }
100
101                let process = binary
102                    .construct()
103                    .stdout(Stdio::piped())
104                    // .stdin(Stdio::piped())
105                    .stderr(Stdio::piped())
106                    .arg(format!("-c '{commands}'"))
107                    .spawn()
108                    .expect("It should be possible to run tests");
109
110                process
111                    .wait_with_output()
112                    .map_err(|_| {
113                        let reason = format!(
114                            "could not execute process {} ({})",
115                            binary, "No execution took place"
116                        );
117
118                        NuError {
119                            desc: reason,
120                            exit: None,
121                            output: None,
122                        }
123                    })
124                    .and_then(|process| {
125                        let out =
126                            Outcome::new(&read_std(&process.stdout), &read_std(&process.stderr));
127
128                        match process.status.success() {
129                            true => Ok(out),
130                            false => Err(NuError {
131                                desc: String::new(),
132                                exit: Some(process.status),
133                                output: Some(out),
134                            }),
135                        }
136                    })
137            }
138            None => Err(NuError {
139                desc: String::from("err"),
140                exit: None,
141                output: None,
142            }),
143        }
144    }
145}
146
147impl fmt::Display for Director {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        write!(f, "director")
150    }
151}
152
153fn read_std(std: &[u8]) -> Vec<u8> {
154    let out = String::from_utf8_lossy(std);
155    let out = out.lines().collect::<Vec<_>>().join("\n");
156    let out = out.replace("\r\n", "");
157    out.replace('\n', "").into_bytes()
158}