1 //! Core of cargo-add command
7 use std
::collections
::BTreeMap
;
8 use std
::collections
::BTreeSet
;
9 use std
::collections
::VecDeque
;
12 use anyhow
::Context
as _
;
13 use cargo_util
::paths
;
14 use indexmap
::IndexSet
;
15 use termcolor
::Color
::Green
;
16 use termcolor
::Color
::Red
;
17 use termcolor
::ColorSpec
;
18 use toml_edit
::Item
as TomlItem
;
20 use crate::core
::dependency
::DepKind
;
21 use crate::core
::registry
::PackageRegistry
;
22 use crate::core
::FeatureValue
;
23 use crate::core
::Package
;
24 use crate::core
::QueryKind
;
25 use crate::core
::Registry
;
26 use crate::core
::Shell
;
27 use crate::core
::Summary
;
28 use crate::core
::Workspace
;
29 use crate::CargoResult
;
31 use crate_spec
::CrateSpec
;
32 use dependency
::Dependency
;
33 use dependency
::GitSource
;
34 use dependency
::PathSource
;
35 use dependency
::RegistrySource
;
36 use dependency
::Source
;
37 use manifest
::LocalManifest
;
39 use crate::ops
::cargo_add
::dependency
::{MaybeWorkspace, WorkspaceSource}
;
40 pub use manifest
::DepTable
;
42 /// Information on what dependencies should be added
43 #[derive(Clone, Debug)]
44 pub struct AddOptions
<'a
> {
45 /// Configuration information for cargo operations
46 pub config
: &'a Config
,
47 /// Package to add dependencies to
48 pub spec
: &'a Package
,
49 /// Dependencies to add or modify
50 pub dependencies
: Vec
<DepOp
>,
51 /// Which dependency section to add these to
52 pub section
: DepTable
,
53 /// Act as if dependencies will be added
57 /// Add dependencies to a manifest
58 pub fn add(workspace
: &Workspace
<'_
>, options
: &AddOptions
<'_
>) -> CargoResult
<()> {
59 let dep_table
= options
66 let manifest_path
= options
.spec
.manifest_path().to_path_buf();
67 let mut manifest
= LocalManifest
::try_new(&manifest_path
)?
;
68 let original_raw_manifest
= manifest
.to_string();
69 let legacy
= manifest
.get_legacy_sections();
70 if !legacy
.is_empty() {
72 "Deprecated dependency sections are unsupported: {}",
77 let mut registry
= PackageRegistry
::new(options
.config
)?
;
80 let _lock
= options
.config
.acquire_package_cache_lock()?
;
81 registry
.lock_patches();
95 .collect
::<CargoResult
<Vec
<_
>>>()?
98 let was_sorted
= manifest
99 .get_table(&dep_table
)
100 .map(TomlItem
::as_table
)
101 .map_or(true, |table_option
| {
102 table_option
.map_or(true, |table
| is_sorted(table
.iter().map(|(name
, _
)| name
)))
105 print_msg(&mut options
.config
.shell(), &dep
, &dep_table
)?
;
106 if let Some(Source
::Path(src
)) = dep
.source() {
107 if src
.path
== manifest
.path
.parent().unwrap_or_else(|| Path
::new("")) {
109 "cannot add `{}` as a dependency to itself",
110 manifest
.package_name()?
115 let available_features
= dep
119 .collect
::<BTreeSet
<&str>>();
120 let mut unknown_features
: Vec
<&str> = Vec
::new();
121 if let Some(req_feats
) = dep
.features
.as_ref() {
122 let req_feats
: BTreeSet
<_
> = req_feats
.iter().map(|s
| s
.as_str()).collect();
123 unknown_features
.extend(req_feats
.difference(&available_features
).copied());
125 if let Some(inherited_features
) = dep
.inherited_features
.as_ref() {
126 let inherited_features
: BTreeSet
<_
> =
127 inherited_features
.iter().map(|s
| s
.as_str()).collect();
128 unknown_features
.extend(inherited_features
.difference(&available_features
).copied());
130 unknown_features
.sort();
131 if !unknown_features
.is_empty() {
132 anyhow
::bail
!("unrecognized features: {unknown_features:?}");
135 manifest
.insert_into_table(&dep_table
, &dep
)?
;
136 manifest
.gc_dep(dep
.toml_key());
140 if let Some(table
) = manifest
141 .get_table_mut(&dep_table
)
143 .and_then(TomlItem
::as_table_like_mut
)
149 if options
.config
.locked() {
150 let new_raw_manifest
= manifest
.to_string();
151 if original_raw_manifest
!= new_raw_manifest
{
153 "the manifest file {} needs to be updated but --locked was passed to prevent this",
154 manifest
.path
.display()
160 options
.config
.shell().warn("aborting add due to dry run")?
;
168 /// Dependency entry operation
169 #[derive(Clone, Debug, PartialEq, Eq)]
171 /// Describes the crate
172 pub crate_spec
: Option
<String
>,
173 /// Dependency key, overriding the package name in crate_spec
174 pub rename
: Option
<String
>,
176 /// Feature flags to activate
177 pub features
: Option
<IndexSet
<String
>>,
178 /// Whether the default feature should be activated
179 pub default_features
: Option
<bool
>,
181 /// Whether dependency is optional
182 pub optional
: Option
<bool
>,
184 /// Registry for looking up dependency version
185 pub registry
: Option
<String
>,
187 /// Git repo for dependency
188 pub path
: Option
<String
>,
189 /// Git repo for dependency
190 pub git
: Option
<String
>,
191 /// Specify an alternative git branch
192 pub branch
: Option
<String
>,
193 /// Specify a specific git rev
194 pub rev
: Option
<String
>,
195 /// Specify a specific git tag
196 pub tag
: Option
<String
>,
199 fn resolve_dependency(
200 manifest
: &LocalManifest
,
205 registry
: &mut PackageRegistry
<'_
>,
206 ) -> CargoResult
<DependencyUI
> {
210 .map(CrateSpec
::resolve
)
212 let mut selected_dep
= if let Some(url
) = &arg
.git
{
213 let mut src
= GitSource
::new(url
);
214 if let Some(branch
) = &arg
.branch
{
215 src
= src
.set_branch(branch
);
217 if let Some(tag
) = &arg
.tag
{
218 src
= src
.set_tag(tag
);
220 if let Some(rev
) = &arg
.rev
{
221 src
= src
.set_rev(rev
);
224 let selected
= if let Some(crate_spec
) = &crate_spec
{
225 if let Some(v
) = crate_spec
.version_req() {
226 // crate specifier includes a version (e.g. `docopt@0.8`)
227 anyhow
::bail
!("cannot specify a git URL (`{url}`) with a version (`{v}`).");
229 let dependency
= crate_spec
.to_dependency()?
.set_source(src
);
230 let selected
= select_package(&dependency
, config
, registry
)?
;
231 if dependency
.name
!= selected
.name
{
232 config
.shell().warn(format
!(
233 "translating `{}` to `{}`",
234 dependency
.name
, selected
.name
,
239 let mut source
= crate::sources
::GitSource
::new(src
.source_id()?
, config
)?
;
240 let packages
= source
.read_packages()?
;
241 let package
= infer_package(packages
, &src
)?
;
242 Dependency
::from(package
.summary())
245 } else if let Some(raw_path
) = &arg
.path
{
246 let path
= paths
::normalize_path(&std
::env
::current_dir()?
.join(raw_path
));
247 let src
= PathSource
::new(&path
);
249 let selected
= if let Some(crate_spec
) = &crate_spec
{
250 if let Some(v
) = crate_spec
.version_req() {
251 // crate specifier includes a version (e.g. `docopt@0.8`)
252 anyhow
::bail
!("cannot specify a path (`{raw_path}`) with a version (`{v}`).");
254 let dependency
= crate_spec
.to_dependency()?
.set_source(src
);
255 let selected
= select_package(&dependency
, config
, registry
)?
;
256 if dependency
.name
!= selected
.name
{
257 config
.shell().warn(format
!(
258 "translating `{}` to `{}`",
259 dependency
.name
, selected
.name
,
264 let source
= crate::sources
::PathSource
::new(&path
, src
.source_id()?
, config
);
265 let packages
= source
.read_packages()?
;
266 let package
= infer_package(packages
, &src
)?
;
267 Dependency
::from(package
.summary())
270 } else if let Some(crate_spec
) = &crate_spec
{
271 crate_spec
.to_dependency()?
273 anyhow
::bail
!("dependency name is required");
275 selected_dep
= populate_dependency(selected_dep
, arg
);
277 let old_dep
= get_existing_dependency(manifest
, selected_dep
.toml_key(), section
)?
;
278 let mut dependency
= if let Some(mut old_dep
) = old_dep
.clone() {
279 if old_dep
.name
!= selected_dep
.name
{
280 // Assuming most existing keys are not relevant when the package changes
281 if selected_dep
.optional
.is_none() {
282 selected_dep
.optional
= old_dep
.optional
;
286 if selected_dep
.source().is_some() {
287 // Overwrite with `crate_spec`
288 old_dep
.source
= selected_dep
.source
;
290 populate_dependency(old_dep
, arg
)
296 if dependency
.source().is_none() {
297 // Checking for a workspace dependency happens first since a member could be specified
298 // in the workspace dependencies table as a dependency
299 if let Some(_dep
) = find_workspace_dep(dependency
.toml_key(), ws
.root_manifest()).ok() {
300 dependency
= dependency
.set_source(WorkspaceSource
::new());
301 } else if let Some(package
) = ws
.members().find(|p
| p
.name().as_str() == dependency
.name
) {
302 // Only special-case workspaces when the user doesn't provide any extra
303 // information, otherwise, trust the user.
304 let mut src
= PathSource
::new(package
.root());
305 // dev-dependencies do not need the version populated
306 if section
.kind() != DepKind
::Development
{
308 let v
= format
!("{op}{version}", version
= package
.version());
309 src
= src
.set_version(v
);
311 dependency
= dependency
.set_source(src
);
313 let latest
= get_latest_dependency(&dependency
, false, config
, registry
)?
;
315 if dependency
.name
!= latest
.name
{
316 config
.shell().warn(format
!(
317 "translating `{}` to `{}`",
318 dependency
.name
, latest
.name
,
320 dependency
.name
= latest
.name
; // Normalize the name
322 dependency
= dependency
.set_source(latest
.source
.expect("latest always has a source"));
326 if let Some(Source
::Workspace(_
)) = dependency
.source() {
327 check_invalid_ws_keys(dependency
.toml_key(), arg
)?
;
330 let version_required
= dependency
.source().and_then(|s
| s
.as_registry()).is_some();
331 let version_optional_in_section
= section
.kind() == DepKind
::Development
;
332 let preserve_existing_version
= old_dep
334 .map(|d
| d
.version().is_some())
336 if !version_required
&& !preserve_existing_version
&& version_optional_in_section
{
337 // dev-dependencies do not need the version populated
338 dependency
= dependency
.clear_version();
341 let query
= dependency
.query(config
)?
;
342 let query
= match query
{
343 MaybeWorkspace
::Workspace(_workspace
) => {
344 let dep
= find_workspace_dep(dependency
.toml_key(), ws
.root_manifest())?
;
345 if let Some(features
) = dep
.features
.clone() {
346 dependency
= dependency
.set_inherited_features(features
);
348 let query
= dep
.query(config
)?
;
350 MaybeWorkspace
::Workspace(_
) => {
351 unreachable
!("This should have been caught when parsing a workspace root")
353 MaybeWorkspace
::Other(query
) => query
,
356 MaybeWorkspace
::Other(query
) => query
,
359 let dependency
= populate_available_features(dependency
, &query
, registry
)?
;
364 /// When { workspace = true } you cannot define other keys that configure
365 /// the source of the dependency such as `version`, `registry`, `registry-index`,
366 /// `path`, `git`, `branch`, `tag`, `rev`, or `package`. You can also not define
367 /// `default-features`.
369 /// Only `default-features`, `registry` and `rename` need to be checked
370 /// for currently. This is because `git` and its associated keys, `path`, and
371 /// `version` should all bee checked before this is called. `rename` is checked
372 /// for as it turns into `package`
373 fn check_invalid_ws_keys(toml_key
: &str, arg
: &DepOp
) -> CargoResult
<()> {
374 fn err_msg(toml_key
: &str, flag
: &str, field
: &str) -> String
{
376 "cannot override workspace dependency with `{flag}`, \
377 either change `workspace.dependencies.{toml_key}.{field}` \
378 or define the dependency exclusively in the package's manifest"
382 if arg
.default_features
.is_some() {
385 err_msg(toml_key
, "--default-features", "default-features")
388 if arg
.registry
.is_some() {
389 anyhow
::bail
!("{}", err_msg(toml_key
, "--registry", "registry"))
391 // rename is `package`
392 if arg
.rename
.is_some() {
393 anyhow
::bail
!("{}", err_msg(toml_key
, "--rename", "package"))
398 /// Provide the existing dependency for the target table
400 /// If it doesn't exist but exists in another table, let's use that as most likely users
401 /// want to use the same version across all tables unless they are renaming.
402 fn get_existing_dependency(
403 manifest
: &LocalManifest
,
406 ) -> CargoResult
<Option
<Dependency
>> {
407 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
416 let mut possible
: Vec
<_
> = manifest
417 .get_dependency_versions(dep_key
)
419 let key
= if path
== *section
{
420 (Key
::Existing
, true)
421 } else if dep
.is_err() {
422 (Key
::Error
, path
.target().is_some())
424 let key
= match path
.kind() {
425 DepKind
::Normal
=> Key
::Normal
,
426 DepKind
::Build
=> Key
::Build
,
427 DepKind
::Development
=> Key
::Dev
,
429 (key
, path
.target().is_some())
434 possible
.sort_by_key(|(key
, _
)| *key
);
435 let (key
, dep
) = if let Some(item
) = possible
.pop() {
442 if key
.0 != Key
::Existing
{
443 // When the dep comes from a different section, we only care about the source and not any
444 // of the other fields, like `features`
446 dep
= Dependency
::new(&unrelated
.name
);
447 dep
.source
= unrelated
.source
.clone();
448 dep
.registry
= unrelated
.registry
.clone();
450 // dev-dependencies do not need the version populated when path is set though we
451 // should preserve it if the user chose to populate it.
452 let version_required
= unrelated
.source().and_then(|s
| s
.as_registry()).is_some();
453 let version_optional_in_section
= section
.kind() == DepKind
::Development
;
454 if !version_required
&& version_optional_in_section
{
455 dep
= dep
.clear_version();
462 fn get_latest_dependency(
463 dependency
: &Dependency
,
464 _flag_allow_prerelease
: bool
,
466 registry
: &mut PackageRegistry
<'_
>,
467 ) -> CargoResult
<Dependency
> {
468 let query
= dependency
.query(config
)?
;
470 MaybeWorkspace
::Workspace(_
) => {
471 unreachable
!("registry dependencies required, found a workspace dependency");
473 MaybeWorkspace
::Other(query
) => {
474 let possibilities
= loop {
475 match registry
.query_vec(&query
, QueryKind
::Fuzzy
) {
476 std
::task
::Poll
::Ready(res
) => {
479 std
::task
::Poll
::Pending
=> registry
.block_until_ready()?
,
482 let latest
= possibilities
485 // Fallback to a pre-release if no official release is available by sorting them as
487 let stable
= s
.version().pre
.is_empty();
488 (stable
, s
.version())
492 "the crate `{dependency}` could not be found in registry index."
495 let mut dep
= Dependency
::from(latest
);
496 if let Some(reg_name
) = dependency
.registry
.as_deref() {
497 dep
= dep
.set_registry(reg_name
);
505 dependency
: &Dependency
,
507 registry
: &mut PackageRegistry
<'_
>,
508 ) -> CargoResult
<Dependency
> {
509 let query
= dependency
.query(config
)?
;
511 MaybeWorkspace
::Workspace(_
) => {
512 unreachable
!("path or git dependency expected, found workspace dependency");
514 MaybeWorkspace
::Other(query
) => {
515 let possibilities
= loop {
516 // Exact to avoid returning all for path/git
517 match registry
.query_vec(&query
, QueryKind
::Exact
) {
518 std
::task
::Poll
::Ready(res
) => {
521 std
::task
::Poll
::Pending
=> registry
.block_until_ready()?
,
524 match possibilities
.len() {
526 let source
= dependency
528 .expect("source should be resolved before here");
529 anyhow
::bail
!("the crate `{dependency}` could not be found at `{source}`")
532 let mut dep
= Dependency
::from(&possibilities
[0]);
533 if let Some(reg_name
) = dependency
.registry
.as_deref() {
534 dep
= dep
.set_registry(reg_name
);
539 let source
= dependency
541 .expect("source should be resolved before here");
543 "unexpectedly found multiple copies of crate `{dependency}` at `{source}`"
551 fn infer_package(mut packages
: Vec
<Package
>, src
: &dyn std
::fmt
::Display
) -> CargoResult
<Package
> {
552 let package
= match packages
.len() {
554 anyhow
::bail
!("no packages found at `{src}`");
556 1 => packages
.pop().expect("match ensured element is present"),
558 let mut names
: Vec
<_
> = packages
560 .map(|p
| p
.name().as_str().to_owned())
562 names
.sort_unstable();
563 anyhow
::bail
!("multiple packages found at `{src}`: {}", names
.join(", "));
569 fn populate_dependency(mut dependency
: Dependency
, arg
: &DepOp
) -> Dependency
{
570 if let Some(registry
) = &arg
.registry
{
571 if registry
.is_empty() {
572 dependency
.registry
= None
;
574 dependency
.registry
= Some(registry
.to_owned());
577 if let Some(value
) = arg
.optional
{
579 dependency
.optional
= Some(true);
581 dependency
.optional
= None
;
584 if let Some(value
) = arg
.default_features
{
586 dependency
.default_features
= None
;
588 dependency
.default_features
= Some(false);
591 if let Some(value
) = arg
.features
.as_ref() {
592 dependency
= dependency
.extend_features(value
.iter().cloned());
595 if let Some(rename
) = &arg
.rename
{
596 dependency
= dependency
.set_rename(rename
);
602 /// Track presentation-layer information with the editable representation of a `[dependencies]`
603 /// entry (Dependency)
604 pub struct DependencyUI
{
605 /// Editable representation of a `[depednencies]` entry
607 /// The version of the crate that we pulled `available_features` from
608 available_version
: Option
<semver
::Version
>,
609 /// The widest set of features compatible with `Dependency`s version requirement
610 available_features
: BTreeMap
<String
, Vec
<String
>>,
614 fn new(dep
: Dependency
) -> Self {
617 available_version
: None
,
618 available_features
: Default
::default(),
622 fn apply_summary(&mut self, summary
: &Summary
) {
623 self.available_version
= Some(summary
.version().clone());
624 self.available_features
= summary
629 k
.as_str().to_owned(),
631 .filter_map(|v
| match v
{
632 FeatureValue
::Feature(f
) => Some(f
.as_str().to_owned()),
633 FeatureValue
::Dep { .. }
| FeatureValue
::DepFeature { .. }
=> None
,
635 .collect
::<Vec
<_
>>(),
642 impl<'s
> From
<&'s Summary
> for DependencyUI
{
643 fn from(other
: &'s Summary
) -> Self {
644 let dep
= Dependency
::from(other
);
645 let mut dep
= Self::new(dep
);
646 dep
.apply_summary(other
);
651 impl std
::fmt
::Display
for DependencyUI
{
652 fn fmt(&self, f
: &mut std
::fmt
::Formatter
<'_
>) -> std
::fmt
::Result
{
657 impl std
::ops
::Deref
for DependencyUI
{
658 type Target
= Dependency
;
660 fn deref(&self) -> &Self::Target
{
665 /// Lookup available features
666 fn populate_available_features(
667 dependency
: Dependency
,
668 query
: &crate::core
::dependency
::Dependency
,
669 registry
: &mut PackageRegistry
<'_
>,
670 ) -> CargoResult
<DependencyUI
> {
671 let mut dependency
= DependencyUI
::new(dependency
);
673 if !dependency
.available_features
.is_empty() {
674 return Ok(dependency
);
677 let possibilities
= loop {
678 match registry
.query_vec(&query
, QueryKind
::Fuzzy
) {
679 std
::task
::Poll
::Ready(res
) => {
682 std
::task
::Poll
::Pending
=> registry
.block_until_ready()?
,
685 // Ensure widest feature flag compatibility by picking the earliest version that could show up
686 // in the lock file for a given version requirement.
687 let lowest_common_denominator
= possibilities
690 // Fallback to a pre-release if no official release is available by sorting them as
692 let is_pre
= !s
.version().pre
.is_empty();
693 (is_pre
, s
.version())
696 anyhow
::format_err
!("the crate `{dependency}` could not be found in registry index.")
698 dependency
.apply_summary(&lowest_common_denominator
);
703 fn print_msg(shell
: &mut Shell
, dep
: &DependencyUI
, section
: &[String
]) -> CargoResult
<()> {
706 if matches
!(shell
.verbosity(), crate::core
::shell
::Verbosity
::Quiet
) {
710 let mut message
= String
::new();
711 write
!(message
, "{}", dep
.name
)?
;
713 Some(Source
::Registry(src
)) => {
714 if src
.version
.chars().next().unwrap_or('
0'
).is_ascii_digit() {
715 write
!(message
, " v{}", src
.version
)?
;
717 write
!(message
, " {}", src
.version
)?
;
720 Some(Source
::Path(_
)) => {
721 write
!(message
, " (local)")?
;
723 Some(Source
::Git(_
)) => {
724 write
!(message
, " (git)")?
;
726 Some(Source
::Workspace(_
)) => {
727 write
!(message
, " (workspace)")?
;
731 write
!(message
, " to")?
;
732 if dep
.optional().unwrap_or(false) {
733 write
!(message
, " optional")?
;
735 let section
= if section
.len() == 1 {
738 format
!("{} for target `{}`", §ion
[2], §ion
[1])
740 write
!(message
, " {section}")?
;
741 write
!(message
, ".")?
;
742 shell
.status("Adding", message
)?
;
744 let mut activated
: IndexSet
<_
> = dep
.features
.iter().flatten().map(|s
| s
.as_str()).collect();
745 if dep
.default_features().unwrap_or(true) {
746 activated
.insert("default");
748 activated
.extend(dep
.inherited_features
.iter().flatten().map(|s
| s
.as_str()));
749 let mut walk
: VecDeque
<_
> = activated
.iter().cloned().collect();
750 while let Some(next
) = walk
.pop_front() {
752 dep
.available_features
756 .map(|s
| s
.as_str()),
759 dep
.available_features
763 .map(|s
| s
.as_str()),
766 activated
.remove("default");
768 let mut deactivated
= dep
771 .filter(|f
| !activated
.contains(f
.as_str()) && *f
!= "default")
772 .collect
::<Vec
<_
>>();
774 if !activated
.is_empty() || !deactivated
.is_empty() {
775 let prefix
= format
!("{:>13}", " ");
776 let suffix
= if let Some(version
) = &dep
.available_version
{
777 let mut version
= version
.clone();
778 version
.build
= Default
::default();
779 let version
= version
.to_string();
780 // Avoid displaying the version if it will visually look like the version req that we
782 let version_req
= dep
784 .and_then(|v
| semver
::VersionReq
::parse(v
).ok())
785 .and_then(|v
| precise_version(&v
));
786 if version_req
.as_deref() != Some(version
.as_str()) {
787 format
!(" as of v{}", version
)
795 format_args
!("{}Features{}:\n", prefix
, suffix
),
798 for feat
in activated
{
799 shell
.write_stderr(&prefix
, &ColorSpec
::new())?
;
800 shell
.write_stderr('
+'
, &ColorSpec
::new().set_bold(true).set_fg(Some(Green
)))?
;
801 shell
.write_stderr(format_args
!(" {}\n", feat
), &ColorSpec
::new())?
;
803 for feat
in deactivated
{
804 shell
.write_stderr(&prefix
, &ColorSpec
::new())?
;
805 shell
.write_stderr('
-'
, &ColorSpec
::new().set_bold(true).set_fg(Some(Red
)))?
;
806 shell
.write_stderr(format_args
!(" {}\n", feat
), &ColorSpec
::new())?
;
813 // Based on Iterator::is_sorted from nightly std; remove in favor of that when stabilized.
814 fn is_sorted(mut it
: impl Iterator
<Item
= impl PartialOrd
>) -> bool
{
815 let mut last
= match it
.next() {
830 fn find_workspace_dep(toml_key
: &str, root_manifest
: &Path
) -> CargoResult
<Dependency
> {
831 let manifest
= LocalManifest
::try_new(root_manifest
)?
;
832 let manifest
= manifest
836 .context("could not make `manifest.data` into a table")?
;
837 let workspace
= manifest
839 .context("could not find `workspace`")?
841 .context("could not make `manifest.data.workspace` into a table")?
;
842 let dependencies
= workspace
844 .context("could not find `dependencies` table in `workspace`")?
846 .context("could not make `dependencies` into a table")?
;
847 let dep_item
= dependencies
.get(toml_key
).context(format
!(
848 "could not find {} in `workspace.dependencies`",
851 Dependency
::from_toml(root_manifest
.parent().unwrap(), toml_key
, dep_item
)
854 /// Convert a `semver::VersionReq` into a rendered `semver::Version` if all fields are fully
856 fn precise_version(version_req
: &semver
::VersionReq
) -> Option
<String
> {
863 // Only ops we can determine a precise version from
865 | semver
::Op
::GreaterEq
869 | semver
::Op
::Wildcard
873 // Only do it when full precision is specified
874 c
.minor
.and_then(|minor
| {
875 c
.patch
.map(|patch
| semver
::Version
{
880 build
: Default
::default(),
885 .map(|v
| v
.to_string())