# cargo config
## Overview
This is a proposal to add a `cargo config` command. Initially it is focused on displaying Cargo's config files, though there are future extensions for writing settings and more.
The `cargo config` command is *only* focused on reading and writing config files. It is not intended for querying other things, like `Cargo.toml` or things like the computed location target platform or target directory. This is a relatively low-level command. One of the major motivations for this command is to provide a debugging tool, to better understand which config settings are set, and where they are set.
## Design
### Actions
The `cargo config` command takes an argument of the action to perform. Initially only `cargo config get` will be implemented. In the future, other actions such as `set` or `edit` may be added.
### `cargo config get`
The `get` action takes an optional TOML dotted key to display. If no key is provided, the root table is displayed (which is all config values).
#### Output formats
The `get` command will be designed to support multiple output formats. The initial implementation may only implement one or two of these, but others should be easy to add. The format can be selected with the `--format` flag (which defaults to "toml"). Some proposed formats are:
* `toml`: Settings are in a TOML-compatible document format, with dotted keys and values. Example:
```console
$ cargo config get build
build.jobs = 1
build.rustc = "rustc"
build.rustflags = ["foo", "bar"]
```
* `json`: Settings are in a JSON format. Examples:
```console
$ cargo config get alias.space_example --format=json
{"alias": {"space_example": ["run", "--release", "--", "command list"]}}
$ cargo config get profile.dev --format=json
{"profile": {"dev": {"lto": true}}}
```
* `toml-value`: Only displays the *value*, without the key. Examples:
```console
$ cargo config get alias.space_example --format=toml-value
["run", "--release", "--", "\"command list\""]
$ cargo config get profile.dev --format=toml-value
{lto = "true", opt-level = 3}
$ cargo config get build --format=toml-value
{jobs = 1, rustc = "rustc", pipelining = true}
```
* `toml-table`: Displays with TOML table syntax. Example:
```console
$ cargo config get profile.dev --format=toml-table
[profile.dev]
lto = "true"
opt-level = 3
```
* `json-value`: Displays just the *value* in JSON syntax. Example:
```console
$ cargo config get alias.space_example --format=json-value
["run", "--release", "--", "command list"]
$ cargo config get profile.dev --format=json-value
{"lto": true}
$ cargo config get profile.dev.lto --format=json-value
true
```
* `sh-value`: Displays just the *value* in a Bourne shell compatible syntax. This can be used for shell scripting. Example:
```console
$ cargo config get alias.space_example --format=sh-value
"run --release -- \"command list\""
$ cargo config get build.rustflags --format=sh-value
"--foo --bar --file \"space example\""
```
#### Origin display
An important part of debugging is to know where each setting originates from. Cargo already tracks these internally in a fine-grained manner. For example, each value of an array may come from a different location. The `--show-origin` option can be used to display the origin of values. The display will depend on the format, the following shows some examples.
```console
$ cargo config get alias --show-origin
alias.b = "build" # /path/to/.cargo/config
alias.foo = ["x", "y"] # /path/to/.cargo/config
build.rustflags = ["--one", # /path/to/.cargo/config
"--two", # /path/to/other/.cargo/config
]
```
It may be important for debugging purposes to display both merged and unmerged origins. The `--merged=BOOL` flag can be used to control whether or not it is merged. An unmerged result may look like this:
```console
$ cargo config get alias --show-origin --merged=no
# /path/to/.cargo/config
alias.b = "build"
alias.foo = "foo"
# /path/to/other/.cargo/config
alias.b = "build2"
alias.bar = "bar"
```
The other formats may be tricky to attach location information to each value. Some of them may not support the merged format. Here are some examples of what these other formats may look like:
```
$ cargo config get build --show-origin --format=toml-value
# /path/to/.cargo/config.toml
{target = "x86_64-unknown-linux-gnu"}
```
```
$ cargo config get build --show-origin --format=json
{"/path/to/.cargo/config.toml": {"build": {"target": "x86_64-unknown-linux-gnu"}}}
```
```
$ cargo config get build --show-origin --format=json-value
{"/path/to/.cargo/config.toml": "x86_64-unknown-linux-gnu"}
```
The `get` action will support reading environment variables and the `--config` CLI argument. Care will need to be taken on how these non-file locations will be displayed.
#### Include handling
The [include](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#config-include) feature may need some special consideration. An `--includes=BOOL` option can be used to indicate whether or not includes are "followed". When it is "on", includes are loaded. When it is "off", they are not loaded.
I'm uncertain exactly how this should behave, or what the defaults should be. Git defaults to "off" for specific files (`--file`, `--global`, etc.), but is otherwise "on".
This may need some experimentation to figure out how it should work.
#### Credential handling
Initially, the `get` action will never load values from the `credentials` files. Perhaps in the future, there can be an option to support that?
## Future possibilities
Additional actions may be added in the future:
* `set`: Setting values is a bit tricky due to the complex nature of TOML tables. Additionally, it will need a comment-preserving TOML editing library. This document doesn't specify exactly how this will work, but perhaps it could be limited to non-table values, with a basic syntax like `cargo config set build.target x86_64-unknown-linux-gnu`.
* *Where* it will be set is also tricky. One option is to set in the most specific directory that contains a `.cargo/config.toml` file, using `~/.cargo/config.toml` if no local files are found.
* `git` returns an error if not inside a repo (instead of editing the global config). Maybe if there is no `.cargo/config.toml` in the current workspace it should be an error?
* The `set` command may take the following options:
* `--file <filename>` — Set in the given file.
* `--global` — Set in `~/.cargo/config.toml`.
* `--workspace` — Set in the root of the current workspace.
* `delete`: The `delete` action will remove a setting. It is not clear how this will handle config file nesting.
* `edit`: The `edit` action can be used to launch an editor to edit a config setting.
The `get` action will probably not support displaying the "default" of a value for several reasons. One is that the default in some cases is the "unset" value, which can be awkward to display in TOML. Another is that the default value in some cases can be complicated to calculate, and are dynamic based on how Cargo is used. Additionally, Cargo internally is not designed to make this information easy to obtain, which may require substantial changes to how the config system works.
## Concerns
Not reading `Cargo.toml` for things like `profile` might be confusing. Maybe include a flag (`--merge-manifest`?) to special-case that? Or always merge it, and have `--manifest=Cargo.toml` as a read only that file?
Some config values are used in complex ways, such as rustflags, which take into consideration the target table, and `RUSTFLAGS`. Naively fetching `build.rustflags` may not get the actual rustflags that will be used. Maybe there could be special "computed" values, like `cargo config get rustflags`. Various things that can be complicated:
* rustflags, rustdocflags
* target-dir
* incremental (environment and profile!)
* cargo-new name and email
* https proxy (and a few other settings like timeout)
* `RUSTC` and `RUSTC_WRAPPER`, etc.
Another option is to add a new subcommand called `cargo query` which can be used for general-purpose querying of things from Cargo, which can handle complex computed values.
## Prior art
This design is influenced by several other programs which provide similar functionality:
* [`git config`](https://git-scm.com/docs/git-config)
* [`npm config`](https://docs.npmjs.com/cli/v6/commands/npm-config)
* [`yarn config`](https://classic.yarnpkg.com/en/docs/cli/config/)
* [`nuget config`](https://docs.microsoft.com/en-us/nuget/reference/cli-reference/cli-ref-config)
* [`aws configure`](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)
* [`bundle config`](https://bundler.io/v2.2/man/bundle-config.1.html)