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