nu_command/misc/
unlet.rs

1use nu_engine::command_prelude::*;
2use nu_protocol::engine::{ENV_VARIABLE_ID, IN_VARIABLE_ID, NU_VARIABLE_ID};
3
4#[derive(Clone)]
5pub struct DeleteVar;
6
7impl Command for DeleteVar {
8    fn name(&self) -> &str {
9        "unlet"
10    }
11
12    fn description(&self) -> &str {
13        "Delete variables from nushell memory, making them unrecoverable."
14    }
15
16    fn signature(&self) -> nu_protocol::Signature {
17        Signature::build("unlet")
18            .input_output_types(vec![(Type::Nothing, Type::Nothing)])
19            .rest(
20                "rest",
21                SyntaxShape::Any,
22                "The variables to delete (pass as $variable_name).",
23            )
24            .category(Category::Experimental)
25    }
26
27    fn run(
28        &self,
29        _engine_state: &EngineState,
30        stack: &mut Stack,
31        call: &Call,
32        _input: PipelineData,
33    ) -> Result<PipelineData, ShellError> {
34        // Collect all positional arguments passed to the command
35        let expressions: Vec<_> = (0..).map_while(|i| call.positional_nth(stack, i)).collect();
36
37        // Ensure at least one argument is provided
38        if expressions.is_empty() {
39            return Err(ShellError::GenericError {
40                error: "Wrong number of arguments".into(),
41                msg: "unlet takes at least one argument".into(),
42                span: Some(call.head),
43                help: None,
44                inner: vec![],
45            });
46        }
47
48        // Validate each argument and collect valid variable IDs
49        let mut var_ids = Vec::with_capacity(expressions.len());
50        for expr in expressions {
51            match &expr.expr {
52                nu_protocol::ast::Expr::Var(var_id) => {
53                    // Prevent deletion of built-in variables that are essential for nushell operation
54                    if var_id == &NU_VARIABLE_ID
55                        || var_id == &ENV_VARIABLE_ID
56                        || var_id == &IN_VARIABLE_ID
57                    {
58                        // Determine the variable name for the error message
59                        let var_name = match *var_id {
60                            NU_VARIABLE_ID => "nu",
61                            ENV_VARIABLE_ID => "env",
62                            IN_VARIABLE_ID => "in",
63                            _ => "unknown", // This should never happen due to the check above
64                        };
65
66                        return Err(ShellError::GenericError {
67                            error: "Cannot delete built-in variable".into(),
68                            msg: format!(
69                                "'${}' is a built-in variable and cannot be deleted",
70                                var_name
71                            ),
72                            span: Some(expr.span),
73                            help: None,
74                            inner: vec![],
75                        });
76                    }
77                    var_ids.push(*var_id);
78                }
79                _ => {
80                    // Argument is not a variable reference
81                    return Err(ShellError::GenericError {
82                        error: "Not a variable".into(),
83                        msg: "Argument must be a variable reference like $x".into(),
84                        span: Some(expr.span),
85                        help: Some("Use $variable_name to refer to the variable".into()),
86                        inner: vec![],
87                    });
88                }
89            }
90        }
91
92        // Remove all valid variables from the stack
93        for var_id in var_ids {
94            stack.remove_var(var_id);
95        }
96
97        Ok(PipelineData::empty())
98    }
99
100    fn requires_ast_for_arguments(&self) -> bool {
101        true
102    }
103
104    fn examples(&self) -> Vec<Example<'_>> {
105        vec![
106            Example {
107                example: "let x = 42; unlet $x",
108                description: "Delete a variable from memory",
109                result: None,
110            },
111            Example {
112                example: "let x = 1; let y = 2; unlet $x $y",
113                description: "Delete multiple variables from memory",
114                result: None,
115            },
116            Example {
117                example: "unlet $nu",
118                description: "Attempting to delete a built-in variable fails",
119                result: None,
120            },
121            Example {
122                example: "unlet 42",
123                description: "Attempting to delete a non-variable fails",
124                result: None,
125            },
126        ]
127    }
128}