Skip to main content

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    // Needed so IR retains the argument Expression for var/cell-path origin lookup.
33    fn requires_ast_for_arguments(&self) -> bool {
34        true
35    }
36
37    fn run(
38        &self,
39        engine_state: &EngineState,
40        stack: &mut Stack,
41        call: &Call,
42        input: PipelineData,
43    ) -> Result<PipelineData, ShellError> {
44        let arg = call.positional_nth(stack, 0);
45        let head = call.head;
46
47        if !matches!(input, PipelineData::Empty)
48            && let Some(arg_expr) = arg
49        {
50            return Err(ShellError::IncompatibleParameters {
51                left_message: "pipeline input was provided".into(),
52                left_span: head,
53                right_message: "but a positional metadata expression was also given".into(),
54                right_span: arg_expr.span,
55            });
56        }
57
58        match arg {
59            Some(Expression {
60                expr: Expr::FullCellPath(full_cell_path),
61                span,
62                ..
63            }) => {
64                if full_cell_path.tail.is_empty() {
65                    match &full_cell_path.head {
66                        Expression {
67                            expr: Expr::Var(var_id),
68                            ..
69                        } => {
70                            let origin = stack.get_var_with_origin(*var_id, *span)?;
71                            Ok(
72                                build_metadata_record_value(&origin, input.metadata_ref(), head)
73                                    .into_pipeline_data(),
74                            )
75                        }
76                        _ => {
77                            let val: Value = call.req(engine_state, stack, 0)?;
78                            Ok(
79                                build_metadata_record_value(&val, input.metadata_ref(), head)
80                                    .into_pipeline_data(),
81                            )
82                        }
83                    }
84                } else {
85                    let val: Value = call.req(engine_state, stack, 0)?;
86                    Ok(
87                        build_metadata_record_value(&val, input.metadata_ref(), head)
88                            .into_pipeline_data(),
89                    )
90                }
91            }
92            Some(_) => {
93                let val: Value = call.req(engine_state, stack, 0)?;
94                Ok(
95                    build_metadata_record_value(&val, input.metadata_ref(), head)
96                        .into_pipeline_data(),
97                )
98            }
99            None => {
100                Ok(Value::record(build_metadata_record(&input, head), head).into_pipeline_data())
101            }
102        }
103    }
104
105    fn examples(&self) -> Vec<Example<'_>> {
106        vec![
107            Example {
108                description: "Get the metadata of a variable.",
109                example: "let a = 42; metadata $a",
110                result: None,
111            },
112            Example {
113                description: "Get the metadata of the input.",
114                example: "ls | metadata",
115                result: None,
116            },
117        ]
118    }
119}
120
121fn build_metadata_record_value(
122    arg: &Value,
123    metadata: Option<&PipelineMetadata>,
124    head: Span,
125) -> Value {
126    let mut record = Record::new();
127    record.push("span", arg.span().into_value(head));
128    Value::record(extend_record_with_metadata(record, metadata, head), head)
129}
130
131#[cfg(test)]
132mod test {
133    use super::*;
134
135    #[test]
136    fn test_examples() -> nu_test_support::Result {
137        nu_test_support::test().examples(Metadata)
138    }
139}