nu_command/debug/
metadata.rs

1use super::util::{build_metadata_record, extend_record_with_metadata};
2use nu_engine::command_prelude::*;
3use nu_protocol::{
4    PipelineMetadata,
5    ast::{Expr, Expression},
6};
7
8#[derive(Clone)]
9pub struct Metadata;
10
11impl Command for Metadata {
12    fn name(&self) -> &str {
13        "metadata"
14    }
15
16    fn description(&self) -> &str {
17        "Get the metadata for items in the stream."
18    }
19
20    fn signature(&self) -> nu_protocol::Signature {
21        Signature::build("metadata")
22            .input_output_types(vec![(Type::Any, Type::record())])
23            .allow_variants_without_examples(true)
24            .optional(
25                "expression",
26                SyntaxShape::Any,
27                "The expression you want metadata for.",
28            )
29            .category(Category::Debug)
30    }
31
32    fn requires_ast_for_arguments(&self) -> bool {
33        true
34    }
35
36    fn run(
37        &self,
38        engine_state: &EngineState,
39        stack: &mut Stack,
40        call: &Call,
41        input: PipelineData,
42    ) -> Result<PipelineData, ShellError> {
43        let arg = call.positional_nth(stack, 0);
44        let head = call.head;
45
46        if !matches!(input, PipelineData::Empty)
47            && let Some(arg_expr) = arg
48        {
49            return Err(ShellError::IncompatibleParameters {
50                left_message: "pipeline input was provided".into(),
51                left_span: head,
52                right_message: "but a positional metadata expression was also given".into(),
53                right_span: arg_expr.span,
54            });
55        }
56
57        match arg {
58            Some(Expression {
59                expr: Expr::FullCellPath(full_cell_path),
60                span,
61                ..
62            }) => {
63                if full_cell_path.tail.is_empty() {
64                    match &full_cell_path.head {
65                        Expression {
66                            expr: Expr::Var(var_id),
67                            ..
68                        } => {
69                            let origin = stack.get_var_with_origin(*var_id, *span)?;
70                            Ok(build_metadata_record_value(
71                                &origin,
72                                input.metadata().as_ref(),
73                                head,
74                            )
75                            .into_pipeline_data())
76                        }
77                        _ => {
78                            let val: Value = call.req(engine_state, stack, 0)?;
79                            Ok(
80                                build_metadata_record_value(&val, input.metadata().as_ref(), head)
81                                    .into_pipeline_data(),
82                            )
83                        }
84                    }
85                } else {
86                    let val: Value = call.req(engine_state, stack, 0)?;
87                    Ok(
88                        build_metadata_record_value(&val, input.metadata().as_ref(), head)
89                            .into_pipeline_data(),
90                    )
91                }
92            }
93            Some(_) => {
94                let val: Value = call.req(engine_state, stack, 0)?;
95                Ok(
96                    build_metadata_record_value(&val, input.metadata().as_ref(), head)
97                        .into_pipeline_data(),
98                )
99            }
100            None => {
101                Ok(Value::record(build_metadata_record(&input, head), head).into_pipeline_data())
102            }
103        }
104    }
105
106    fn examples(&self) -> Vec<Example<'_>> {
107        vec![
108            Example {
109                description: "Get the metadata of a variable",
110                example: "let a = 42; metadata $a",
111                result: None,
112            },
113            Example {
114                description: "Get the metadata of the input",
115                example: "ls | metadata",
116                result: None,
117            },
118        ]
119    }
120}
121
122fn build_metadata_record_value(
123    arg: &Value,
124    metadata: Option<&PipelineMetadata>,
125    head: Span,
126) -> Value {
127    let mut record = Record::new();
128    record.push("span", arg.span().into_value(head));
129    Value::record(extend_record_with_metadata(record, metadata, head), head)
130}
131
132#[cfg(test)]
133mod test {
134    use super::*;
135
136    #[test]
137    fn test_examples() {
138        use crate::test_examples;
139
140        test_examples(Metadata {})
141    }
142}