Skip to content

Commit

Permalink
[stdlib-candidate] set-env (#787)
Browse files Browse the repository at this point in the history
Rewrite of nushell/nushell#12156 for jdx/mise#1763.

### Why?

Nushell philosophically omits a `set` list mutation. But `$env` is
inherently mutable leading to issues described in nushell/nushell#12148.
`set-env` provides such an operation exclusively for `$env`.

### What changed?

1. Explicit flag instead of implicit list concatenation
2. Expands updates to any `$env` field not only `$env.config`

### How is it used?

```yaml
❯ set-env -h
Gracefully set an environment variable or merge a nested option.

Examples:
  Set $env.NUPM_HOME
  > set-env NUPM_HOME $'($nu.home-path)/.local/share/nupm'

  Add to $env.NU_LIB_DIRS
  > set-env --append NU_LIB_DIRS $'($env.NUPM_HOME)/modules'

  Set a nested config option
  > set-env config.filesize.metric true

  Add a config hook
  > set-env -a config.hooks.pre_prompt 'ellie | print'

Usage:
  > main {flags} <field> <value>

Flags:
  -a, --append - Append to the previous value or wrap in a new list
  -h, --help - Display the help message for this command

Parameters:
  field <cell-path>: The environment variable name or nested option cell path
  value <any>: The value to set or append

Input/output types:
  ╭───┬─────────┬─────────╮
  │ # │  input  │ output  │
  ├───┼─────────┼─────────┤
  │ 0 │ nothing │ nothing │
  ╰───┴─────────┴─────────╯
```

### How does it work?

```nushell
export def --env main [
  field: cell-path
  value: any
  --append (-a)
]: nothing -> nothing {

  # just an alias
  def 'get or' [default field] {
    get --ignore-errors $field | default $default
  }

  let value = if $append {
    # append to the previous value or empty list
    $env | get or [] $field | append $value
  } else {
    $value
  }

  # work around nushell/nushell#12168
  let field = $field | to text | split row .
  let value = match $field {

    [_] => $value
    # if cell path is nested
    [$root, ..$field] => {
      let field = $field | into cell-path

      # reassigning $env would be an error
      # merging reserved names like PWD would be an error
      # so merge from 1 level deep instead
      $env | get or {} $root | upsert $field $value
    }
  }

  # avoid issues noted above
  load-env { ($field | first): $value }
}
```

### Where are the tests?

Pending next PR for nupm integration.
  • Loading branch information
texastoland authored Mar 12, 2024
1 parent 5de1346 commit 558f4ba
Showing 1 changed file with 37 additions and 0 deletions.
37 changes: 37 additions & 0 deletions stdlib-candidate/set-env.nu
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Gracefully set an environment variable or merge a nested option.
#
# Examples:
# Set $env.NUPM_HOME
# > set-env NUPM_HOME $'($nu.home-path)/.local/share/nupm'
#
# Add to $env.NU_LIB_DIRS
# > set-env --append NU_LIB_DIRS $'($env.NUPM_HOME)/modules'
#
# Set a nested config option
# > set-env config.filesize.metric true
#
# Add a config hook
# > set-env -a config.hooks.pre_prompt 'ellie | print'
export def --env main [
field: cell-path # The environment variable name or nested option cell path
value: any # The value to set or append
--append (-a) # Append to the previous value or wrap in a new list
]: nothing -> nothing {
def 'get or' [default field] {
get --ignore-errors $field | default $default
}
let value = if $append {
$env | get or [] $field | append $value
} else {
$value
}
let field = $field | to text | split row .
let value = match $field {
[_] => $value
[$root, ..$field] => {
let field = $field | into cell-path
$env | get or {} $root | upsert $field $value
}
}
load-env { ($field | first): $value }
}

0 comments on commit 558f4ba

Please sign in to comment.