nu_cmd_lang/core_commands/attr/
deprecated.rs

1use nu_cmd_base::WrapCall;
2use nu_engine::command_prelude::*;
3
4#[derive(Clone)]
5pub struct AttrDeprecated;
6
7impl Command for AttrDeprecated {
8    fn name(&self) -> &str {
9        "attr deprecated"
10    }
11
12    fn signature(&self) -> Signature {
13        Signature::build("attr deprecated")
14            .input_output_type(Type::Nothing, Type::record())
15            .optional(
16                "message",
17                SyntaxShape::String,
18                "Help message to include with deprecation warning.",
19            )
20            .named(
21                "flag",
22                SyntaxShape::String,
23                "Mark a flag as deprecated rather than the command",
24                None,
25            )
26            .named(
27                "since",
28                SyntaxShape::String,
29                "Denote a version when this item was deprecated",
30                Some('s'),
31            )
32            .named(
33                "remove",
34                SyntaxShape::String,
35                "Denote a version when this item will be removed",
36                Some('r'),
37            )
38            .param(
39                Flag::new("report")
40                    .arg(SyntaxShape::String)
41                    .desc("How to warn about this item. One of: first (default), every")
42                    .completion(Completion::new_list(&["first", "every"])),
43            )
44            .category(Category::Core)
45    }
46
47    fn description(&self) -> &str {
48        "Attribute for marking a command or flag as deprecated."
49    }
50
51    fn extra_description(&self) -> &str {
52        "\
53            Mark a command (default) or flag/switch (--flag) as deprecated. \
54            By default, only the first usage will trigger a deprecation warning.\n\
55            \n\
56            A help message can be included to provide more context for the deprecation, \
57            such as what to use as a replacement.\n\
58            \n\
59            Also consider setting the category to deprecated with @category deprecated\
60        "
61    }
62
63    fn run(
64        &self,
65        engine_state: &EngineState,
66        stack: &mut Stack,
67        call: &Call,
68        _input: PipelineData,
69    ) -> Result<PipelineData, ShellError> {
70        let call = WrapCall::Eval(engine_state, stack, call);
71        Ok(deprecated_record(call)?.into_pipeline_data())
72    }
73
74    fn run_const(
75        &self,
76        working_set: &StateWorkingSet,
77        call: &Call,
78        _input: PipelineData,
79    ) -> Result<PipelineData, ShellError> {
80        let call = WrapCall::ConstEval(working_set, call);
81        Ok(deprecated_record(call)?.into_pipeline_data())
82    }
83
84    fn is_const(&self) -> bool {
85        true
86    }
87
88    fn examples(&self) -> Vec<Example<'_>> {
89        vec![
90            Example {
91                description: "Add a deprecation warning to a custom command",
92                example: r###"@deprecated
93    def outdated [] {}"###,
94                result: Some(Value::nothing(Span::test_data())),
95            },
96            Example {
97                description: "Add a deprecation warning with a custom message",
98                example: r###"@deprecated "Use my-new-command instead."
99    @category deprecated
100    def my-old-command [] {}"###,
101                result: Some(Value::string(
102                    "Use my-new-command instead.",
103                    Span::test_data(),
104                )),
105            },
106        ]
107    }
108}
109
110fn deprecated_record(call: WrapCall) -> Result<Value, ShellError> {
111    let (call, message): (_, Option<Spanned<String>>) = call.opt(0)?;
112    let (call, flag): (_, Option<Spanned<String>>) = call.get_flag("flag")?;
113    let (call, since): (_, Option<Spanned<String>>) = call.get_flag("since")?;
114    let (call, remove): (_, Option<Spanned<String>>) = call.get_flag("remove")?;
115    let (call, report): (_, Option<Spanned<String>>) = call.get_flag("report")?;
116
117    let mut record = Record::new();
118    if let Some(message) = message {
119        record.push("help", Value::string(message.item, message.span))
120    }
121    if let Some(flag) = flag {
122        record.push("flag", Value::string(flag.item, flag.span))
123    }
124    if let Some(since) = since {
125        record.push("since", Value::string(since.item, since.span))
126    }
127    if let Some(remove) = remove {
128        record.push("expected_removal", Value::string(remove.item, remove.span))
129    }
130
131    let report = if let Some(Spanned { item, span }) = report {
132        match item.as_str() {
133            "every" => Value::string(item, span),
134            "first" => Value::string(item, span),
135            _ => {
136                return Err(ShellError::IncorrectValue {
137                    msg: "The report mode must be one of: every, first".into(),
138                    val_span: span,
139                    call_span: call.head(),
140                });
141            }
142        }
143    } else {
144        Value::string("first", call.head())
145    };
146    record.push("report", report);
147
148    Ok(Value::record(record, call.head()))
149}