Tool configs in Cargo.toml (e.g. rustfmt, clippy) and alternatives brainstorming

As more tools become available to the rust community, each tool adopts a new mytool.toml configuration file, which leads to a proliferation of config files at the root of a typical project (or sometimes subproject in a workspace).

Intuitively, I prefer to minimize the number of configuration files present at the root of my project. I'll try to put that intuition a bit into words, but hopefully others will also be able to come along with more directly informed experiences to reinforce the motivation.

  • More files, particularly at a project root, can make it more difficult for a newcomer to scan a explore a project's file directory to figure out where to get started.

  • Particularily, if each tool adopts its own non-standard config file naming scheme it beomes more difficult for scan for files of a particular file you may be looking for.

    Documentation files tend to be easy to read by adopting a consistent naming scheme, e.g. README, CONTRIBUTING, and LICENSE. These particularly also benefit from being widely adopted outside of the rust community. Additionally, it is desirable for documentation to be separated in this way to accommodate the different use cases that are common for newcomers to the project (How do I use this? How do I add to this? Am I allowed to use this?).

    Common config files (at least so far) have much less consistent naming, and even sometimes specifically support variable naming for backwards compatibility purposes: e.g. Cargo.toml, .rustfmt.toml, my-tool.toml, another_tool.toml, ThirdTool.toml.

    Comparative to documenation files, tool configuration files have a much narrower use-- the most common is that a user doesn't care and shouldn't need to look at tool configuration files. Ideally the configuration is written once, read never, and the tooling performs all of the relevant work on behalf of users.

    The two use cases I see most common that you'd want to look at configuration files are:

    • A contributor would like to see what workflows this project uses beyond/differently than the base common tooling. In this scenario, if the majority of the configuration is in one file, a developer can look in one place and be made aware of all differences. Similarly if a project is using a custom tool, it could be called out as a tool explicitly rather than the higher learning curve of "what is frobulator.toml for"?

    • A contributor would like to change project workflows.

  • More "community standard" configuration files increases the learning curve around project organization for newcomers to rust; although this is specifically when stepping in to an existing project, rather than learning via cargo new which doesn't impose any knowledge on the subject.

This problem is substantially mitigated, because Rust tools tend to reach a very high bar in terms of design and usability before becoming widely adopted by the community; and additionally tend to have widely applicable defaults that are useful for a majority of cases. However desiring just a single custom config rule leads to managing/teaching about a new config file.

Prior work

The NPM community (known perhaps for have too much and non-standardized tooling) has nonetheless come up with the prior art of supporting the majority of lint/test/format configuration in the package.json file (most equivalent to Cargo.toml, specifically dependency management). The NPM communities approach to this is fragmented because it is a de-facto rather than explicit standard; in particular the config subsections are inconsistently named (e.g. eslintConfig, browserlist, jest).

Idea - Add [tools] section in Cargo.toml

N.B. as noted in @sflacker's and @steveklabnik's quick feedback, there is an existing [package.metadata] section which behaves in the same way as described below.

Instead--- would we want to update rustfmt and clippy to support loading their configuration from this section, and update surrounding documentation to encourage new tools to do the same.

Perhaps we could add develop a cargo-config crate which automates a community recommended configuration config standard which could for example, automatically look in Cargo.toml for metadata as well as .<tool>.toml and tool.toml like rustfmt and clippy do currently.

A tools section in the cargo manifest would provide a standard place to store config files for both community blesses as well as custom or experimental tools.

Documentation about the [tools] section in the cargo manifest will inform the normative behavior for naming the config subsection to be [tools.<tool_name>], e.g. [tools.rustfmt] or [tools.clippy]. Cargo would mostly ignore this section, but other optional standards could also be introduced to integrate the tools.

Example:

[package]
name = "mycrate"

[tools.rustfmt]
reorder_imports = false

[tools.clippy]
blacklisted-names = ["toto", "tata", "titi"]

In many, I feel this is similar extension compared to tool lints and attributes.

Alternative - More configuration as code

Another reasonable alternative is to try to continue to expand the capabilities or just standardization of the "configuration as code pattern" (largely used by clippy in the form of tool_lints, although it also has its own config file for some things which I believe cannot be expressed as attributes such as clippy's blacklisted-names).

Specifically, we could move more tool configuration in to {main,lib}.rs attributes.

Possible syntax:

#![rustfmt::config(reorder_imports = false)]

Looking forward to feedback!

7 Likes

The [package.metadata] section of Cargo.toml is already reserved for this kind of thing. Your example would look like

[package]
name = "mycrate"

[package.metadata.rustfmt]
reorder_imports = false

[package.metadata.clippy]
blacklisted-names = ["toto", "tata", "titi"]
9 Likes

For an example of a crate that does this: https://github.com/rust-osdev/bootimage#configuration

I missed that section, but yeah that makes sense.

The follow up would be, is it desirable to have rustfmt and clippy respect configuration defined in [package.metadata.rustfmt] and [package.metadata.clippy] respectively.

Separately, should it then be encouraged specifically by updating the documentation for the [package.metadata] section and in clippy and rustfmt's configuration section? (Possibly, not initially, but overtime after getting more experience with combined vs separated config files?)

1 Like

Updated the opening post.

As a follow up, if there was consensus about using [package.metadata], it might make sense to have a community recommended cargo_config (or similar) crate that automatically looks in the recommended places, possibly including:

  • The [package.metdata.<tool>] key in Cargo.toml
  • The <tool>.toml and .<tool>.toml like rustfmt and clippy do currently.
1 Like

Related prior work: Python specifies a pyproject.toml that has a [tools] section.

3 Likes

I would love to see this situation become a reality.

Specifically, the current situation for us right now is:

  • rust-toolchain to specify the Rust version of a project
  • rustfmt.toml for rustfmt specific configuration
  • no config file support for Clippy, requiring all configs to be defined in the library/application code itself (repeated, when using a workspace)

It would be great if (at least) these three could be unified in some way. I guess rust-toolchain is a bit of an odd ball in this list, and it’s the one that causes the least amount of “pain” in this situation, as far as I’m concerned.

cargo-deb uses the [package.metadata] approach. For it, and similar tools, I’m maintaining the cargo_toml crate, which is a simple Serde-compatible definition that can read Cargo.toml with your custom metadata.

1 Like

I have a use-case for standalone config files: I have my editor set up so that if a rustfmt.toml or .rustfmt.toml file exists, and the rustfmt executable is present, it will automatically reformat files as I save them.

I could permanently enable auto-formatting, but sometimes I contribute to projects that don’t use auto-formatting, and it would be rude to include formatting changes in a non-formatting-related pull request.

I could enable auto-formatting manually, but then it wouldn’t be very “auto”.

I could add another clause to my test, like "Cargo.toml contains a line matching ^\s*\[package.metadata.rustfmt[].] but it feels wrong for an empty section in a config file to be semantically significant… more wrong than an empty file, at any rate.

I would feel better about this if I could query Cargo.toml via cargo, instead of trying to interpret complex structured data with regexes. Perhaps something like a cargo metadata --uses-tool rustfmt command that returns 0 if there’s evidence of a package.metadata.rustfmt section (or any other convention) in Cargo.toml.

(I don’t have this same issue with Clippy, because Clippy does not change file content so I’m happy to leave it permanently enabled)

One problem with [package.metadata] is that it does not support virtual workspaces.

I’m not convinced yet that Cargo.toml is the right place for tool configuration. If an organization wants to share settings across projects, but they don’t use a monolithic workspace, then they wouldn’t be able to share them. That may not be a common enough need, though. What are the considerations of using Cargo.toml over .cargo/config?

I recently wrote up some questions regarding lint configuration here: https://github.com/rust-lang/cargo/pull/5728#issuecomment-447073103

2 Likes

I’m currently working on a lint configuration Mini-RFC with @detrumi, that would be mildly related to this proposal. It’s good to see other people thinking about this :slight_smile:

@ehuss One of the advantages of taking Cargo.toml over .cargo/config is that users already have a Cargo.toml in their projects. I suppose almost no-one has a .cargo/config file. It would probably lead to a situation where people are confused as to what configuration goes into which file? But yes, as you noted it would not be possible to share files across projects, unless we add a new inherits_from key or something like that to Cargo.toml.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.