mirror of
https://github.com/nushell/nushell.git
synced 2025-08-09 00:44:57 +02:00
Convert Path to list
in main and preserve case (#14764)
# Description Fixes multiple issues related to `ENV_CONVERSION` and path-conversion-to-list. * #14681 removed some calls to `convert_env_values()`, but we found that this caused `nu -n` to no longer convert the path properly. * `ENV_CONVERSIONS` have apparently never preserved case, meaning a conversion with a key of `foo` would not update `$env.FOO` but rather create a new environment variable with a different case. * There was a partial code-path that attempted to solve this for `PATH`, but it only worked for `PATH` and `Path`. * `convert_env_values()`, which handled `ENV_CONVERSIONS` was called in multiple places in the startup depending on flags. This PR: * Refactors the startup to handle the conversion in `main()` rather than in each potential startup path * Updates `get_env_var_insensitive()` functions added in #14390 to return the name of the environment variable with its original case. This allows code that updates environment variables to preserve the case. * Makes use of the updated function in `ENV_CONVERSIONS` to preserve the case of any updated environment variables. The `ENV_CONVERSION` key itself is still case **insensitive**. * Makes use of the updated function to preserve the case of the `PATH` environment variable (normally handled separately, regardless of whether or not there was an `ENV_CONVERSION` for it). ## Before `env_convert_values` was run: * Before the user `env.nu` ran, which included `nu -c <commandstring>` and `nu <script.nu>` * Before the REPL loaded, which included `nu -n` ## After `env_convert_values` always runs once in `main()` before any config file is processed or the REPL is started # User-Facing Changes Bug fixes # Tests + Formatting - 🟢 `toolkit fmt` - 🟢 `toolkit clippy` - 🟢 `toolkit test` - 🟢 `toolkit test stdlib` Added additional tests to prevent future regression. # After Submitting There is additional cleanup that should probably be done in `convert_env_values()`. This function previously handled `ENV_CONVERSIONS`, but there is no longer any need for this since `convert_env_vars()` runs whenever `$env.ENV_CONVERSIONS` changes now. This means that the only relevant task in the old `convert_env_values()` is to convert the `PATH` to a list, and ensure that it is a list of strings. It's still calling the `from_string` conversion on every variable (just once) even though there are no `ENV_CONVERSIONS` at this point. Leaving that to another PR though, while we get the core issue fixed with this one.
This commit is contained in:
@ -32,7 +32,9 @@ pub fn convert_env_vars(
|
||||
) -> Result<(), ShellError> {
|
||||
let conversions = conversions.as_record()?;
|
||||
for (key, conversion) in conversions.into_iter() {
|
||||
if let Some(val) = stack.get_env_var_insensitive(engine_state, key) {
|
||||
if let Some((case_preserve_env_name, val)) =
|
||||
stack.get_env_var_insensitive(engine_state, key)
|
||||
{
|
||||
match val.get_type() {
|
||||
Type::String => {}
|
||||
_ => continue,
|
||||
@ -52,7 +54,7 @@ pub fn convert_env_vars(
|
||||
.run_with_value(val.clone())?
|
||||
.into_value(val.span())?;
|
||||
|
||||
stack.add_env_var(key.clone(), new_val);
|
||||
stack.add_env_var(case_preserve_env_name.to_string(), new_val);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@ -64,7 +66,10 @@ pub fn convert_env_vars(
|
||||
/// It returns Option instead of Result since we do want to translate all the values we can and
|
||||
/// skip errors. This function is called in the main() so we want to keep running, we cannot just
|
||||
/// exit.
|
||||
pub fn convert_env_values(engine_state: &mut EngineState, stack: &Stack) -> Result<(), ShellError> {
|
||||
pub fn convert_env_values(
|
||||
engine_state: &mut EngineState,
|
||||
stack: &mut Stack,
|
||||
) -> Result<(), ShellError> {
|
||||
let mut error = None;
|
||||
|
||||
let mut new_scope = HashMap::new();
|
||||
@ -89,7 +94,7 @@ pub fn convert_env_values(engine_state: &mut EngineState, stack: &Stack) -> Resu
|
||||
}
|
||||
}
|
||||
|
||||
error = error.or_else(|| ensure_path(&mut new_scope, "PATH"));
|
||||
error = error.or_else(|| ensure_path(engine_state, stack));
|
||||
|
||||
if let Ok(last_overlay_name) = &stack.last_overlay_name() {
|
||||
if let Some(env_vars) = Arc::make_mut(&mut engine_state.env_vars).get_mut(last_overlay_name)
|
||||
@ -242,7 +247,7 @@ pub fn path_str(
|
||||
span: Span,
|
||||
) -> Result<String, ShellError> {
|
||||
let (pathname, pathval) = match stack.get_env_var_insensitive(engine_state, "path") {
|
||||
Some(v) => Ok((if cfg!(windows) { "Path" } else { "PATH" }, v)),
|
||||
Some((_, v)) => Ok((if cfg!(windows) { "Path" } else { "PATH" }, v)),
|
||||
None => Err(ShellError::EnvVarNotFoundAtRuntime {
|
||||
envvar_name: if cfg!(windows) {
|
||||
"Path".to_string()
|
||||
@ -362,11 +367,11 @@ fn get_converted_value(
|
||||
)
|
||||
}
|
||||
|
||||
fn ensure_path(scope: &mut HashMap<String, Value>, env_path_name: &str) -> Option<ShellError> {
|
||||
fn ensure_path(engine_state: &EngineState, stack: &mut Stack) -> Option<ShellError> {
|
||||
let mut error = None;
|
||||
|
||||
// If PATH/Path is still a string, force-convert it to a list
|
||||
if let Some(value) = scope.get(env_path_name) {
|
||||
if let Some((preserve_case_name, value)) = stack.get_env_var_insensitive(engine_state, "Path") {
|
||||
let span = value.span();
|
||||
match value {
|
||||
Value::String { val, .. } => {
|
||||
@ -375,15 +380,17 @@ fn ensure_path(scope: &mut HashMap<String, Value>, env_path_name: &str) -> Optio
|
||||
.map(|p| Value::string(p.to_string_lossy().to_string(), span))
|
||||
.collect();
|
||||
|
||||
scope.insert(env_path_name.to_string(), Value::list(paths, span));
|
||||
stack.add_env_var(preserve_case_name.to_string(), Value::list(paths, span));
|
||||
}
|
||||
Value::List { vals, .. } => {
|
||||
// Must be a list of strings
|
||||
if !vals.iter().all(|v| matches!(v, Value::String { .. })) {
|
||||
error = error.or_else(|| {
|
||||
Some(ShellError::GenericError {
|
||||
error: format!("Wrong {env_path_name} environment variable value"),
|
||||
msg: format!("{env_path_name} must be a list of strings"),
|
||||
error: format!(
|
||||
"Incorrect {preserve_case_name} environment variable value"
|
||||
),
|
||||
msg: format!("{preserve_case_name} must be a list of strings"),
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
@ -398,8 +405,8 @@ fn ensure_path(scope: &mut HashMap<String, Value>, env_path_name: &str) -> Optio
|
||||
|
||||
error = error.or_else(|| {
|
||||
Some(ShellError::GenericError {
|
||||
error: format!("Wrong {env_path_name} environment variable value"),
|
||||
msg: format!("{env_path_name} must be a list of strings"),
|
||||
error: format!("Incorrect {preserve_case_name} environment variable value"),
|
||||
msg: format!("{preserve_case_name} must be a list of strings"),
|
||||
span: Some(span),
|
||||
help: None,
|
||||
inner: vec![],
|
||||
|
Reference in New Issue
Block a user