Skip to main content

nu_command/misc/
unlet.rs

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