Skip to content

Commit f3036b8

Browse files
authored
Allow keeping selected environment variables from removed overlay (#6007)
* Allow keeping selected env from removed overlay * Remove some duplicate code * Change --keep-all back to --keep-custom Because, apparently, you cannot have a named flag called --keep-all, otherwise tests fail? * Fix missing line and wrong test value
1 parent 9b6b817 commit f3036b8

3 files changed

Lines changed: 84 additions & 14 deletions

File tree

crates/nu-command/src/core_commands/overlay/remove.rs

Lines changed: 39 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
11
use nu_engine::CallExt;
22
use nu_protocol::ast::Call;
33
use nu_protocol::engine::{Command, EngineState, Stack};
4-
use nu_protocol::{
5-
Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape, Value,
6-
};
4+
use nu_protocol::{Category, Example, PipelineData, ShellError, Signature, Spanned, SyntaxShape};
75

86
#[derive(Clone)]
97
pub struct OverlayRemove;
@@ -22,9 +20,15 @@ impl Command for OverlayRemove {
2220
.optional("name", SyntaxShape::String, "Overlay to remove")
2321
.switch(
2422
"keep-custom",
25-
"Keep newly added symbols within the next activated overlay",
23+
"Keep all newly added symbols within the next activated overlay",
2624
Some('k'),
2725
)
26+
.named(
27+
"keep-env",
28+
SyntaxShape::List(Box::new(SyntaxShape::String)),
29+
"List of environment variables to keep from the removed overlay",
30+
Some('e'),
31+
)
2832
.category(Category::Core)
2933
}
3034

@@ -60,30 +64,44 @@ impl Command for OverlayRemove {
6064
));
6165
}
6266

63-
if call.has_flag("keep-custom") {
67+
let keep_env: Option<Vec<Spanned<String>>> =
68+
call.get_flag(engine_state, stack, "keep-env")?;
69+
70+
let env_vars_to_keep = if call.has_flag("keep-custom") {
6471
if let Some(overlay_id) = engine_state.find_overlay(overlay_name.item.as_bytes()) {
6572
let overlay_frame = engine_state.get_overlay(overlay_id);
6673
let origin_module = engine_state.get_module(overlay_frame.origin);
6774

68-
let env_vars_to_keep: Vec<(String, Value)> = stack
75+
stack
6976
.get_overlay_env_vars(engine_state, &overlay_name.item)
7077
.into_iter()
7178
.filter(|(name, _)| !origin_module.has_env_var(name.as_bytes()))
72-
.collect();
73-
74-
stack.remove_overlay(&overlay_name.item);
75-
76-
for (name, val) in env_vars_to_keep {
77-
stack.add_env_var(name, val);
78-
}
79+
.collect()
7980
} else {
8081
return Err(ShellError::OverlayNotFoundAtRuntime(
8182
overlay_name.item,
8283
overlay_name.span,
8384
));
8485
}
86+
} else if let Some(env_var_names_to_keep) = keep_env {
87+
let mut env_vars_to_keep = vec![];
88+
89+
for name in env_var_names_to_keep.into_iter() {
90+
match stack.get_env_var(engine_state, &name.item) {
91+
Some(val) => env_vars_to_keep.push((name.item, val.clone())),
92+
None => return Err(ShellError::EnvVarNotFoundAtRuntime(name.item, name.span)),
93+
}
94+
}
95+
96+
env_vars_to_keep
8597
} else {
86-
stack.remove_overlay(&overlay_name.item);
98+
vec![]
99+
};
100+
101+
stack.remove_overlay(&overlay_name.item);
102+
103+
for (name, val) in env_vars_to_keep {
104+
stack.add_env_var(name, val);
87105
}
88106

89107
Ok(PipelineData::new(call.head))
@@ -112,6 +130,13 @@ impl Command for OverlayRemove {
112130
overlay remove"#,
113131
result: None,
114132
},
133+
Example {
134+
description: "Keep the current working directory when removing an overlay",
135+
example: r#"overlay new spam
136+
cd some-dir
137+
overlay remove --keep-env [ PWD ] spam"#,
138+
result: None,
139+
},
115140
]
116141
}
117142
}

crates/nu-protocol/src/value/from_value.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,35 @@ impl FromValue for Vec<String> {
238238
}
239239
}
240240

241+
impl FromValue for Vec<Spanned<String>> {
242+
fn from_value(v: &Value) -> Result<Self, ShellError> {
243+
// FIXME: we may want to fail a little nicer here
244+
match v {
245+
Value::List { vals, .. } => vals
246+
.iter()
247+
.map(|val| match val {
248+
Value::String { val, span } => Ok(Spanned {
249+
item: val.clone(),
250+
span: *span,
251+
}),
252+
c => Err(ShellError::CantConvert(
253+
"string".into(),
254+
c.get_type().to_string(),
255+
c.span()?,
256+
None,
257+
)),
258+
})
259+
.collect::<Result<Vec<Spanned<String>>, ShellError>>(),
260+
v => Err(ShellError::CantConvert(
261+
"string".into(),
262+
v.get_type().to_string(),
263+
v.span()?,
264+
None,
265+
)),
266+
}
267+
}
268+
}
269+
241270
impl FromValue for Vec<bool> {
242271
fn from_value(v: &Value) -> Result<Self, ShellError> {
243272
match v {

tests/overlays/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,3 +507,19 @@ fn overlay_new() {
507507
assert_eq!(actual.out, "spam");
508508
assert_eq!(actual_repl.out, "spam");
509509
}
510+
511+
#[test]
512+
fn overlay_keep_pwd() {
513+
let inp = &[
514+
r#"overlay new spam"#,
515+
r#"cd samples"#,
516+
r#"overlay remove --keep-env [ PWD ] spam"#,
517+
r#"$env.PWD | path basename"#,
518+
];
519+
520+
let actual = nu!(cwd: "tests/overlays", pipeline(&inp.join("; ")));
521+
let actual_repl = nu_repl("tests/overlays", inp);
522+
523+
assert_eq!(actual.out, "samples");
524+
assert_eq!(actual_repl.out, "samples");
525+
}

0 commit comments

Comments
 (0)