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    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(
71                                build_metadata_record_value(&origin, input.metadata_ref(), head)
72                                    .into_pipeline_data(),
73                            )
74                        }
75                        _ => {
76                            let val: Value = call.req(engine_state, stack, 0)?;
77                            Ok(
78                                build_metadata_record_value(&val, input.metadata_ref(), head)
79                                    .into_pipeline_data(),
80                            )
81                        }
82                    }
83                } else {
84                    let val: Value = call.req(engine_state, stack, 0)?;
85                    Ok(
86                        build_metadata_record_value(&val, input.metadata_ref(), head)
87                            .into_pipeline_data(),
88                    )
89                }
90            }
91            Some(_) => {
92                let val: Value = call.req(engine_state, stack, 0)?;
93                Ok(
94                    build_metadata_record_value(&val, input.metadata_ref(), head)
95                        .into_pipeline_data(),
96                )
97            }
98            None => {
99                Ok(Value::record(build_metadata_record(&input, head), head).into_pipeline_data())
100            }
101        }
102    }
103
104    fn examples(&self) -> Vec<Example<'_>> {
105        vec![
106            Example {
107                description: "Get the metadata of a variable.",
108                example: "let a = 42; metadata $a",
109                result: None,
110            },
111            Example {
112                description: "Get the metadata of the input.",
113                example: "ls | metadata",
114                result: None,
115            },
116        ]
117    }
118}
119
120fn build_metadata_record_value(
121    arg: &Value,
122    metadata: Option<&PipelineMetadata>,
123    head: Span,
124) -> Value {
125    let mut record = Record::new();
126    record.push("span", arg.span().into_value(head));
127    Value::record(extend_record_with_metadata(record, metadata, head), head)
128}
129
130#[cfg(test)]
131mod test {
132    use super::*;
133
134    #[test]
135    fn test_examples() -> nu_test_support::Result {
136        nu_test_support::test().examples(Metadata)
137    }
138}