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 `-F` short 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 
