nu_command/system/
complete.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::OutDest;
3
4#[derive(Clone)]
5pub struct Complete;
6
7impl Command for Complete {
8    fn name(&self) -> &str {
9        "complete"
10    }
11
12    fn signature(&self) -> Signature {
13        Signature::build("complete")
14            .category(Category::System)
15            .input_output_types(vec![(Type::Any, Type::record())])
16    }
17
18    fn description(&self) -> &str {
19        "Capture the outputs and exit code from an external piped in command in a nushell table."
20    }
21
22    fn extra_description(&self) -> &str {
23        r#"In order to capture stdout, stderr, and exit_code, externally piped in commands need to be wrapped with `do`"#
24    }
25
26    fn run(
27        &self,
28        _engine_state: &EngineState,
29        _stack: &mut Stack,
30        call: &Call,
31        input: PipelineData,
32    ) -> Result<PipelineData, ShellError> {
33        let head = call.head;
34        match input {
35            PipelineData::ByteStream(stream, ..) => {
36                let Ok(child) = stream.into_child() else {
37                    return Err(ShellError::GenericError {
38                        error: "Complete only works with external commands".into(),
39                        msg: "complete only works on external commands".into(),
40                        span: Some(call.head),
41                        help: None,
42                        inner: vec![],
43                    });
44                };
45
46                let output = child.wait_with_output()?;
47                let exit_code = output.exit_status.code();
48                let mut record = Record::new();
49
50                if let Some(stdout) = output.stdout {
51                    record.push(
52                        "stdout",
53                        match String::from_utf8(stdout) {
54                            Ok(str) => Value::string(str, head),
55                            Err(err) => Value::binary(err.into_bytes(), head),
56                        },
57                    );
58                }
59
60                if let Some(stderr) = output.stderr {
61                    record.push(
62                        "stderr",
63                        match String::from_utf8(stderr) {
64                            Ok(str) => Value::string(str, head),
65                            Err(err) => Value::binary(err.into_bytes(), head),
66                        },
67                    );
68                }
69
70                record.push("exit_code", Value::int(exit_code.into(), head));
71
72                Ok(Value::record(record, call.head).into_pipeline_data())
73            }
74            // bubble up errors from the previous command
75            PipelineData::Value(Value::Error { error, .. }, _) => Err(*error),
76            _ => Err(ShellError::GenericError {
77                error: "Complete only works with external commands".into(),
78                msg: "complete only works on external commands".into(),
79                span: Some(head),
80                help: None,
81                inner: vec![],
82            }),
83        }
84    }
85
86    fn examples(&self) -> Vec<Example> {
87        vec![Example {
88            description: "Run the external command to completion, capturing stdout, stderr, and exit_code",
89            example: "^external arg1 | complete",
90            result: None,
91        }]
92    }
93
94    fn pipe_redirection(&self) -> (Option<OutDest>, Option<OutDest>) {
95        (Some(OutDest::PipeSeparate), Some(OutDest::PipeSeparate))
96    }
97}