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
-Z
to 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
dependencies
table 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
--rename
will remove a prior--rename
-
--optional
is never removed (cargo-edit#298) - Only explicit
--features
removes a prior one -
--vers
,--path
, and--git
overwrite 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
-
rustfmt
is 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
-
--vers
is redundant with@
and is a non-obvious name- Due to
--version
/-V
flags though--version
will 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
--path
and--git
as redundant
-
<name>@<version-req>
is common in external prior art butcargo
tends 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
--vers
and--git
flags - git branch is specified via a URL fragment, instead of a
--branch
- No
--upgrade <policy>
argument to control the version requirement operator - No
--sort
to force sorting of dependencies
-
-
(Javascript) yarn add
-
name@data
where data can be version, git (with fragment for branch), etc -
-E
/--exact
,-T
/--tilde
,-C
/--caret
to control version requirement operator instead of--upgrade <policy>
(also controlled throughdefaultSemverRangePrefix
in config)
-
--cached
for using the lock file (cargo add
: Be clever and infer version from Cargo.lock #41)
- In addition to
--dev
, it has--prefer-dev
which will only add the dependency if it doesn't already exist independencies
as well asdev-dependencies
-
--mode update-lockfile
will 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
/-E
for 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
/-v
instead of--vers
(we use--vers
because of--version
/-V
) -
--source
instead ofpath
(path
correlates to manifest field) - Uses
--git
/--branch
likecargo-add
- Uses
-
(Dart) pub add
- Uses
--git-url
instead of--git
- Uses
--git-ref
instead 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:
-
--sort
has 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-prerelease
has 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 -
--vers
was 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 git
feature 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