nu_command/debug/
debug_.rs

1use nu_engine::command_prelude::*;
2
3#[derive(Clone)]
4pub struct Debug;
5
6impl Command for Debug {
7    fn name(&self) -> &str {
8        "debug"
9    }
10
11    fn description(&self) -> &str {
12        "Debug print the value(s) piped in."
13    }
14
15    fn signature(&self) -> Signature {
16        Signature::build("debug")
17            .input_output_types(vec![
18                (
19                    Type::List(Box::new(Type::Any)),
20                    Type::List(Box::new(Type::String)),
21                ),
22                (Type::Any, Type::String),
23            ])
24            .category(Category::Debug)
25            .switch("raw", "Prints the raw value representation", Some('r'))
26            .switch(
27                "raw-value",
28                "Prints the raw value representation but not the nushell value part",
29                Some('v'),
30            )
31    }
32
33    fn run(
34        &self,
35        engine_state: &EngineState,
36        stack: &mut Stack,
37        call: &Call,
38        input: PipelineData,
39    ) -> Result<PipelineData, ShellError> {
40        let head = call.head;
41        let config = stack.get_config(engine_state);
42        let raw = call.has_flag(engine_state, stack, "raw")?;
43        let raw_value = call.has_flag(engine_state, stack, "raw-value")?;
44
45        // Should PipelineData::empty() result in an error here?
46
47        input.map(
48            move |x| {
49                if raw {
50                    Value::string(x.to_debug_string(), head)
51                } else if raw_value {
52                    match x.coerce_into_string_all() {
53                        Ok(s) => Value::string(format!("{s:#?}"), head),
54                        Err(e) => Value::error(e, head),
55                    }
56                } else {
57                    Value::string(x.to_expanded_string(", ", &config), head)
58                }
59            },
60            engine_state.signals(),
61        )
62    }
63
64    fn examples(&self) -> Vec<Example<'_>> {
65        vec![
66            Example {
67                description: "Debug print a string",
68                example: "'hello' | debug",
69                result: Some(Value::test_string("hello")),
70            },
71            Example {
72                description: "Debug print a list",
73                example: "['hello'] | debug",
74                result: Some(Value::list(
75                    vec![Value::test_string("hello")],
76                    Span::test_data(),
77                )),
78            },
79            Example {
80                description: "Debug print a table",
81                example: "[[version patch]; ['0.1.0' false] ['0.1.1' true] ['0.2.0' false]] | debug",
82                result: Some(Value::list(
83                    vec![
84                        Value::test_string("{version: 0.1.0, patch: false}"),
85                        Value::test_string("{version: 0.1.1, patch: true}"),
86                        Value::test_string("{version: 0.2.0, patch: false}"),
87                    ],
88                    Span::test_data(),
89                )),
90            },
91            Example {
92                description: "Debug print an ansi escape encoded string and get the raw value",
93                example: "$'(ansi red)nushell(ansi reset)' | debug -v",
94                result: Some(Value::test_string("\"\\u{1b}[31mnushell\\u{1b}[0m\"")),
95            },
96        ]
97    }
98}
99
100// This is just a local Value Extension trait to avoid having to
101// put another *_to_string() converter in nu_protocol
102trait ValueExt {
103    fn coerce_into_string_all(&self) -> Result<String, ShellError>;
104    fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError>;
105}
106
107impl ValueExt for Value {
108    fn cant_convert_to<T>(&self, typ: &str) -> Result<T, ShellError> {
109        Err(ShellError::CantConvert {
110            to_type: typ.into(),
111            from_type: self.get_type().to_string(),
112            span: self.span(),
113            help: None,
114        })
115    }
116
117    fn coerce_into_string_all(&self) -> Result<String, ShellError> {
118        let span = self.span();
119        match self {
120            Value::Bool { val, .. } => Ok(val.to_string()),
121            Value::Int { val, .. } => Ok(val.to_string()),
122            Value::Float { val, .. } => Ok(val.to_string()),
123            Value::String { val, .. } => Ok(val.to_string()),
124            Value::Glob { val, .. } => Ok(val.to_string()),
125            Value::Filesize { val, .. } => Ok(val.get().to_string()),
126            Value::Duration { val, .. } => Ok(val.to_string()),
127            Value::Date { val, .. } => Ok(val.to_rfc3339_opts(chrono::SecondsFormat::Nanos, true)),
128            Value::Range { val, .. } => Ok(val.to_string()),
129            Value::Record { val, .. } => Ok(format!(
130                "{{{}}}",
131                val.iter()
132                    .map(|(x, y)| match y.coerce_into_string_all() {
133                        Ok(value) => format!("{x}: {value}"),
134                        Err(err) => format!("Error: {err}"),
135                    })
136                    .collect::<Vec<_>>()
137                    .join(", ")
138            )),
139            Value::List { vals, .. } => Ok(format!(
140                "[{}]",
141                vals.iter()
142                    .map(|x| match x.coerce_into_string_all() {
143                        Ok(value) => value,
144                        Err(err) => format!("Error: {err}"),
145                    })
146                    .collect::<Vec<_>>()
147                    .join(", ")
148            )),
149            Value::Binary { val, .. } => match String::from_utf8(val.to_vec()) {
150                Ok(s) => Ok(s),
151                Err(err) => Value::binary(err.into_bytes(), span).cant_convert_to("string"),
152            },
153            Value::CellPath { val, .. } => Ok(val.to_string()),
154            Value::Nothing { .. } => Ok("nothing".to_string()),
155            val => val.cant_convert_to("string"),
156        }
157    }
158}
159
160#[cfg(test)]
161mod test {
162    #[test]
163    fn test_examples() {
164        use super::Debug;
165        use crate::test_examples;
166        test_examples(Debug {})
167    }
168}