In preparation for merging cargo-add into cargo, I want to solicit input on the CLI before we restrict its evolution by shipping it with cargo.
As a reminder, our options are
- Remove features (for now or forever)
- Make features unstable (require
-Zto use) - Re-work features
Background
cargo add allows you to add or modify dependencies in your Cargo.toml file.
Example commands
cargo add regex
cargo add regex serde
cargo add regex@1 --dev
cargo add regex@~1.0 --build
cargo add regex --vers 1.0 --optional
cargo add regex --upgrade minor # for some reason this is the policy name for `^`
cargo add https://github.com/rust-lang/regex
cargo add regex --git https://github.com/rust-lang/regex
cargo add ../dependency
cargo add dep --path ../dependency
For full --help output, see the README
Particular points
- Effectively there are two modes
- Fill in any relevant field for one package
- Add multiple packages, erroring for fields that are package-specific (
--vers,--features, etc)
- We infer if the
dependenciestable is sorted and preserve that sorting when adding a new dependency -
Adding a workspace dependency
- dev-dependencies always use path
- all other dependencies use version + path
- Re-add to modify an existing entry
- No
--renamewill remove a prior--rename -
--optionalis never removed (cargo-edit#298) - Only explicit
--featuresremoves a prior one -
--vers,--path, and--gitoverwrite each other
- No
Known Points of Concern
1. This is feature rife with minutia that could become a maintenance burden for the cargo team
There are many different users with different needs and different formatting styles. We get a lot of feedback about trying to maintain the existing style or changing a default here or there.
Examples:
- "cargo add" should use dotted keys if already used in
Cargo.toml#521 - cargo add: use predominant string style #217
-
cargo add: include only MAJOR.MINOR #126
Proposal: We adopt a couple of guiding principles
- cargo-add is meant to help users on the default path; users always can edit
Cargo.toml - cargo-add should be opinionated in style but not get in the way of a user communicating intent
-
rustfmtis a prime example of this. A lot of syntax minutia is handled for you but you can still organize your code to clarify intent (e.g. whitespace, ordering, etc).
-
- Impact:
- Use a consistent style run-to-run independent of the existing
Cargo.toml(e.g. string and table styles) - Have a default version requirement style that users explicitly override per call (part of "default" path)
- Sorting is something we will infer and preserve since that is tied more into the organization than in the minutia
- Close out the above issues as
wont-fix - We will not re-format existing entries but leave that to a future
rustfmt for Cargo.toml(this means removing--sort)
- Use a consistent style run-to-run independent of the existing
2. --upgrade <policy> isn't quite clear
The policy names don't map well to what Rust developers are used to (operator characters) and the policy name for ^ isn't correct, calling it minor.
Proposal: Remove --upgrade <policy>
- Most people use or should use the default policy
- Redundant with
--vers <req>and<name>@<req>except when no version is specified and cargo-add looks up the latest. If some does want to specify it, they are most likely to specify=which means they probably have a reason and a specific version in mind rather than "latest".
3. Is our story around pre-release strong enough to have --allow-preleease in the default path?
See Changing Cargo semver compatibility for pre-releases for one case where we need to improve on pre-release. With clap3, this was a frustration point.
Proposal: Remove the flag (or make it unstable)
- Pre-releases are uncommon enough that requiring people to explicitly set the version rather than "pick latest" seems sufficient
- Pre-releases can break from one to the next, so people probably should be explicit about what they are intending to use
4. The sourcing / constraining parts of the CLI don't seem quite polished
-
--versis redundant with@and is a non-obvious name- Due to
--version/-Vflags though--versionwill go away when this is upstreamed into cargo
- Due to
- We overload the positional argument between name, path, and git url
- Have to disambiguate them
- git url limits future evolution because we can't tell it apart from anything else
- This leaves
--pathand--gitas redundant
-
<name>@<version-req>is common in external prior art butcargotends to use:for pkg ids, maybe we should do the same
Proposal:
- As positional, accept zero or more
<name>[:<version-req>]or<path> - Accept
--git <url>[#<type>=<ref>]where type is one ofbranch,tag,orrev- If possible, we auto detect the type from the remote and allow
#<ref>(maybe only do this?) - I chose to use a fragment since its client side, it won't conflict with any requirements from the server
- Will get name from remote if needed
- Alternative, replace
--git <url>withgit+<url>positional like Poetry
- If possible, we auto detect the type from the remote and allow
- Remove
--branch(now handled through fragment) -
--vers(specified as part of<name>[:<version-req>]or extracted from<path>. Chance of needing it in other cases is low
Prior Art
-
(Python) poetry add
-
git+is needed to specify git dependencies - Has dry-run, which we lack
- Doesn't have separate
--versand--gitflags - git branch is specified via a URL fragment, instead of a
--branch - No
--upgrade <policy>argument to control the version requirement operator - No
--sortto force sorting of dependencies
-
-
(Javascript) yarn add
-
name@datawhere data can be version, git (with fragment for branch), etc -
-E/--exact,-T/--tilde,-C/--caretto control version requirement operator instead of--upgrade <policy>(also controlled throughdefaultSemverRangePrefixin config)
-
--cachedfor using the lock file (cargo add: Be clever and infer version from Cargo.lock #41)
- In addition to
--dev, it has--prefer-devwhich will only add the dependency if it doesn't already exist independenciesas well asdev-dependencies -
--mode update-lockfilewill ensure the lock file gets updated as well
-
- (Javascript) npm doesn't have a native solution
-
(Javascript) pnpm-add
- Specify version with
@<version> - Also overloads
<name>[@<version>]with path and repo- Supports a git host-specific protocol for shorthand, like
github:user/repo - Uses fragment for git ref, seems to have some kind of special semver syntax for tags?
- Supports a git host-specific protocol for shorthand, like
- Only supports
--save-exact/-Efor operators outside of the default
- Specify version with
-
(Go) go get
- Specify version with
@<version> - Remove dependency with
@none
- Specify version with
- (Haskell) stack doesn't seem to have a native solution
- (Julia) pkg Add
-
(Ruby) bundle add
- Uses
--version/-vinstead of--vers(we use--versbecause of--version/-V) -
--sourceinstead ofpath(pathcorrelates to manifest field) - Uses
--git/--branchlikecargo-add
- Uses
-
(Dart) pub add
- Uses
--git-urlinstead of--git - Uses
--git-refinstead of--branch,--tag,--rev
- Uses
Status Update
(As of Feb 3, 2022)
You can check out the -h output or install it via cargo install --git https://github.com/killercup/cargo-edit cargo-edit
Summary of UI changes:
-
--sorthas been removed as part of decoupling reformatting from adding -
--upgrade <policy>has been removed due to the unclear interface and low probability of need -
--allow-prereleasehas been removed awaiting further work on our pre-release story in cargo. It wasn't marked unstable because it is a relatively small feature that seems to have limited application, so it didn't seem worth it - Git URLs as positional arguments has been removed as detecting them is ambiguous and limits future evolution (any other kind of URLs we might want to accept)
-
This was reverted, we'll instead request cargo to add support for<name>@<req>has switched to<name>:<req>as a parallel with the pkgid syntax@to pkgids -
--verswas removed in favor of<name>:<req>due to being redundant and reduces the difference between the "multiple add" vs "single add" modes, making it easier to explain -
--git <url>is now unstable and has been put behind-Z gitfeature flag as we work out how we want to expose git support -
Added
-Fshort for `--features and we'll request other cargo commands to update accordingly -
Unstable support for inline feature activation (e.g.
cargo add serde +derive serde_json)- An alternative not-implemented proposal is for position-sensitive options
- Overwrite mode is polished (calling cargo add multiple times on same dep)
- Toggle default features on/off
- Features get appended
- Toggling of optional
- And preserving of unspecified fields (1 , 2 , 3 ).
- Reuse version reqs from other tables
for removing 
