1use crate::{
5 Error, PackageId,
6 graph::{
7 BuildTargetImpl, BuildTargetKindImpl, DepRequiredOrOptional, DependencyReqImpl,
8 NamedFeatureDep, OwnedBuildTargetId, PackageGraph, PackageGraphData, PackageIx,
9 PackageLinkImpl, PackageMetadataImpl, PackagePublishImpl, PackageSourceImpl, WorkspaceImpl,
10 cargo_version_matches,
11 },
12 sorted_set::SortedSet,
13};
14use ahash::AHashMap;
15use camino::{Utf8Path, Utf8PathBuf};
16use cargo_metadata::{
17 DepKindInfo, Dependency, DependencyKind, Metadata, Node, NodeDep, Package, Target,
18};
19use fixedbitset::FixedBitSet;
20use indexmap::{IndexMap, IndexSet};
21use once_cell::sync::OnceCell;
22use petgraph::prelude::*;
23use semver::{Version, VersionReq};
24use smallvec::SmallVec;
25use std::{
26 borrow::Cow,
27 cell::RefCell,
28 collections::{BTreeMap, HashSet},
29 rc::Rc,
30};
31use target_spec::TargetSpec;
32
33impl PackageGraph {
34 pub(crate) fn build(mut metadata: Metadata) -> Result<Self, Box<Error>> {
36 let resolve_nodes = metadata.resolve.map(|r| r.nodes).unwrap_or_default();
38
39 let workspace_members: HashSet<_> = metadata
40 .workspace_members
41 .into_iter()
42 .map(PackageId::from_metadata)
43 .collect();
44
45 let workspace_root = metadata.workspace_root;
46
47 let mut build_state = GraphBuildState::new(
48 &mut metadata.packages,
49 resolve_nodes,
50 &workspace_root,
51 &workspace_members,
52 )?;
53
54 let packages: AHashMap<_, _> = metadata
55 .packages
56 .into_iter()
57 .map(|package| build_state.process_package(package))
58 .collect::<Result<_, _>>()?;
59
60 let dep_graph = build_state.finish();
61
62 let workspace = WorkspaceImpl::new(
63 workspace_root,
64 metadata.target_directory,
65 metadata.workspace_metadata,
66 &packages,
67 workspace_members,
68 )?;
69
70 Ok(Self {
71 dep_graph,
72 sccs: OnceCell::new(),
73 feature_graph: OnceCell::new(),
74 data: PackageGraphData {
75 packages,
76 workspace,
77 },
78 })
79 }
80}
81
82impl WorkspaceImpl {
83 fn new(
85 workspace_root: impl Into<Utf8PathBuf>,
86 target_directory: impl Into<Utf8PathBuf>,
87 metadata_table: serde_json::Value,
88 packages: &AHashMap<PackageId, PackageMetadataImpl>,
89 members: impl IntoIterator<Item = PackageId>,
90 ) -> Result<Self, Box<Error>> {
91 use std::collections::btree_map::Entry;
92
93 let workspace_root = workspace_root.into();
94 let mut members_by_path = BTreeMap::new();
97 let mut members_by_name = BTreeMap::new();
98 for id in members {
99 let package_metadata = packages.get(&id).ok_or_else(|| {
101 Error::PackageGraphConstructError(format!("workspace member '{}' not found", id))
102 })?;
103
104 let workspace_path = match &package_metadata.source {
105 PackageSourceImpl::Workspace(path) => path,
106 _ => {
107 return Err(Error::PackageGraphConstructError(format!(
108 "workspace member '{}' at path {:?} not in workspace",
109 id, package_metadata.manifest_path,
110 ))
111 .into());
112 }
113 };
114 members_by_path.insert(workspace_path.to_path_buf(), id.clone());
115
116 match members_by_name.entry(package_metadata.name.clone().into_boxed_str()) {
117 Entry::Vacant(vacant) => {
118 vacant.insert(id.clone());
119 }
120 Entry::Occupied(occupied) => {
121 return Err(Error::PackageGraphConstructError(format!(
122 "duplicate package name in workspace: '{}' is name for '{}' and '{}'",
123 occupied.key(),
124 occupied.get(),
125 id
126 ))
127 .into());
128 }
129 }
130 }
131
132 Ok(Self {
133 root: workspace_root,
134 target_directory: target_directory.into(),
135 metadata_table,
136 members_by_path,
137 members_by_name,
138 #[cfg(feature = "proptest1")]
139 name_list: OnceCell::new(),
140 })
141 }
142}
143
144struct GraphBuildState<'a> {
146 dep_graph: Graph<PackageId, PackageLinkImpl, Directed, PackageIx>,
147 package_data: AHashMap<PackageId, Rc<PackageDataValue>>,
148 by_package_name: AHashMap<String, Vec<Rc<PackageDataValue>>>,
150
151 resolve_data: AHashMap<PackageId, Vec<NodeDep>>,
154 workspace_root: &'a Utf8Path,
155 workspace_members: &'a HashSet<PackageId>,
156}
157
158impl<'a> GraphBuildState<'a> {
159 fn new(
161 packages: &mut [Package],
162 resolve_nodes: Vec<Node>,
163 workspace_root: &'a Utf8Path,
164 workspace_members: &'a HashSet<PackageId>,
165 ) -> Result<Self, Box<Error>> {
166 let edge_count = resolve_nodes
168 .iter()
169 .map(|node| node.deps.len())
170 .sum::<usize>();
171
172 let mut dep_graph = Graph::with_capacity(packages.len(), edge_count);
173 let all_package_data: AHashMap<_, _> = packages
174 .iter_mut()
175 .map(|package| PackageDataValue::new(package, &mut dep_graph))
176 .collect::<Result<_, _>>()?;
177
178 let mut by_package_name: AHashMap<String, Vec<Rc<PackageDataValue>>> =
181 AHashMap::with_capacity(all_package_data.len());
182 for package_data in all_package_data.values() {
183 by_package_name
184 .entry(package_data.name.clone())
185 .or_default()
186 .push(package_data.clone());
187 }
188
189 let resolve_data: AHashMap<_, _> = resolve_nodes
190 .into_iter()
191 .map(|node| {
192 (
193 PackageId::from_metadata(node.id),
194 node.deps,
197 )
198 })
199 .collect();
200
201 Ok(Self {
202 dep_graph,
203 package_data: all_package_data,
204 by_package_name,
205 resolve_data,
206 workspace_root,
207 workspace_members,
208 })
209 }
210
211 fn process_package(
212 &mut self,
213 package: Package,
214 ) -> Result<(PackageId, PackageMetadataImpl), Box<Error>> {
215 let package_id = PackageId::from_metadata(package.id);
216 let (package_data, build_targets) =
217 self.package_data_and_remove_build_targets(&package_id)?;
218
219 let source = if self.workspace_members.contains(&package_id) {
220 PackageSourceImpl::Workspace(self.workspace_path(&package_id, &package.manifest_path)?)
221 } else if let Some(source) = package.source {
222 if source.is_crates_io() {
223 PackageSourceImpl::CratesIo
224 } else {
225 PackageSourceImpl::External(source.repr.into())
226 }
227 } else {
228 let dirname = match package.manifest_path.parent() {
230 Some(dirname) => dirname,
231 None => {
232 return Err(Error::PackageGraphConstructError(format!(
233 "package '{}': manifest path '{}' does not have parent",
234 package_id, package.manifest_path,
235 ))
236 .into());
237 }
238 };
239 PackageSourceImpl::create_path(dirname, self.workspace_root)
240 };
241
242 let resolved_deps = self.resolve_data.remove(&package_id).unwrap_or_default();
244
245 let dep_resolver = DependencyResolver::new(
246 &package_id,
247 &self.package_data,
248 &self.by_package_name,
249 &package.dependencies,
250 );
251
252 for NodeDep {
253 name: resolved_name,
254 pkg,
255 dep_kinds,
256 ..
257 } in resolved_deps
258 {
259 let dep_id = PackageId::from_metadata(pkg);
260 let (dep_data, deps) = dep_resolver.resolve(&resolved_name, &dep_id, &dep_kinds)?;
261 let link = PackageLinkImpl::new(&package_id, &resolved_name, deps)?;
262 self.dep_graph
266 .update_edge(package_data.package_ix, dep_data.package_ix, link);
267 }
268
269 let has_default_feature = package.features.contains_key("default");
270
271 let optional_deps: IndexSet<_> = package
282 .dependencies
283 .into_iter()
284 .filter_map(|dep| {
285 if dep.optional {
286 match dep.rename {
287 Some(rename) => Some(rename.into_boxed_str()),
288 None => Some(dep.name.into_boxed_str()),
289 }
290 } else {
291 None
292 }
293 })
294 .collect();
295
296 let mut seen_explicit = FixedBitSet::with_capacity(optional_deps.len());
298
299 let mut named_features: IndexMap<_, _> = package
301 .features
302 .into_iter()
303 .map(|(feature_name, deps)| {
304 let mut parsed_deps = SmallVec::with_capacity(deps.len());
305 for dep in deps {
306 let dep = NamedFeatureDep::from_cargo_string(dep);
307 if let NamedFeatureDep::OptionalDependency(d) = &dep {
308 let index = optional_deps.get_index_of(d.as_ref()).ok_or_else(|| {
309 Error::PackageGraphConstructError(format!(
310 "package '{}': named feature {} specifies 'dep:{d}', but {d} is not an optional dependency",
311 package_id,
312 feature_name,
313 d = d))
314 })?;
315 seen_explicit.set(index, true);
316 }
317 parsed_deps.push(dep);
318 }
319 Ok((feature_name.into_boxed_str(), parsed_deps))
320 })
321 .collect::<Result<_, Error>>()?;
322
323 for (index, dep) in optional_deps.iter().enumerate() {
325 if !seen_explicit.contains(index) {
326 named_features.insert(
327 dep.clone(),
328 std::iter::once(NamedFeatureDep::OptionalDependency(dep.clone())).collect(),
329 );
330 }
331 }
332
333 let rust_version_req = package
336 .rust_version
337 .as_ref()
338 .map(|rust_version| VersionReq {
339 comparators: vec![semver::Comparator {
340 op: semver::Op::GreaterEq,
341 major: rust_version.major,
342 minor: Some(rust_version.minor),
343 patch: Some(rust_version.patch),
344 pre: semver::Prerelease::EMPTY,
346 }],
347 });
348
349 Ok((
350 package_id,
351 PackageMetadataImpl {
352 name: package.name,
353 version: package.version,
354 authors: package.authors,
355 description: package.description.map(|s| s.into()),
356 license: package.license.map(|s| s.into()),
357 license_file: package.license_file.map(|f| f.into()),
358 manifest_path: package.manifest_path.into(),
359 categories: package.categories,
360 keywords: package.keywords,
361 readme: package.readme.map(|s| s.into()),
362 repository: package.repository.map(|s| s.into()),
363 homepage: package.homepage.map(|s| s.into()),
364 documentation: package.documentation.map(|s| s.into()),
365 edition: package.edition.to_string().into_boxed_str(),
366 metadata_table: package.metadata,
367 links: package.links.map(|s| s.into()),
368 publish: PackagePublishImpl::new(package.publish),
369 default_run: package.default_run.map(|s| s.into()),
370 rust_version: package.rust_version,
371 rust_version_req,
372 named_features,
373 optional_deps,
374
375 package_ix: package_data.package_ix,
376 source,
377 build_targets,
378 has_default_feature,
379 },
380 ))
381 }
382
383 fn package_data_and_remove_build_targets(
384 &self,
385 id: &PackageId,
386 ) -> Result<(Rc<PackageDataValue>, BuildTargetMap), Box<Error>> {
387 let package_data = self.package_data.get(id).ok_or_else(|| {
388 Error::PackageGraphConstructError(format!("no package data found for package '{}'", id))
389 })?;
390 let package_data = package_data.clone();
391 let build_targets = std::mem::take(&mut *package_data.build_targets.borrow_mut());
392 Ok((package_data, build_targets))
393 }
394
395 fn workspace_path(
397 &self,
398 id: &PackageId,
399 manifest_path: &Utf8Path,
400 ) -> Result<Box<Utf8Path>, Box<Error>> {
401 let workspace_path = pathdiff::diff_utf8_paths(manifest_path, self.workspace_root)
403 .ok_or_else(|| {
404 Error::PackageGraphConstructError(format!(
405 "failed to find path from workspace (root: {}) to member '{id}' at path {manifest_path}",
406 self.workspace_root
407 ))
408 })?;
409 let workspace_path = workspace_path.parent().ok_or_else(|| {
410 Error::PackageGraphConstructError(format!(
411 "workspace member '{}' has invalid manifest path {:?}",
412 id, manifest_path
413 ))
414 })?;
415 Ok(convert_relative_forward_slashes(workspace_path).into_boxed_path())
416 }
417
418 fn finish(self) -> Graph<PackageId, PackageLinkImpl, Directed, PackageIx> {
419 self.dep_graph
420 }
421}
422
423#[derive(Debug)]
425struct PackageDataValue {
426 package_ix: NodeIndex<PackageIx>,
427 name: String,
428 resolved_name: ResolvedName,
429 build_targets: RefCell<BuildTargetMap>,
432 version: Version,
433}
434
435impl PackageDataValue {
436 fn new(
437 package: &mut Package,
438 dep_graph: &mut Graph<PackageId, PackageLinkImpl, Directed, PackageIx>,
439 ) -> Result<(PackageId, Rc<Self>), Box<Error>> {
440 let package_id = PackageId::from_metadata(package.id.clone());
441 let package_ix = dep_graph.add_node(package_id.clone());
442
443 let mut build_targets = BuildTargets::new(&package_id);
445 for build_target in package.targets.drain(..) {
446 build_targets.add(build_target)?;
447 }
448 let build_targets = build_targets.finish();
449
450 let resolved_name = match build_targets.get(&OwnedBuildTargetId::Library) {
451 Some(target) => {
452 let lib_name = target
453 .lib_name
454 .as_deref()
455 .expect("lib_name is always specified for library targets");
456 if lib_name != package.name {
457 ResolvedName::LibNameSpecified(lib_name.to_string())
458 } else {
459 ResolvedName::LibNameNotSpecified(lib_name.replace('-', "_"))
461 }
462 }
463 None => {
464 ResolvedName::NoLibTarget
468 }
469 };
470
471 let value = PackageDataValue {
472 package_ix,
473 name: package.name.clone(),
474 resolved_name,
475 build_targets: RefCell::new(build_targets),
476 version: package.version.clone(),
477 };
478
479 Ok((package_id, Rc::new(value)))
480 }
481}
482
483#[derive(Clone, Debug, Eq, PartialEq, Hash)]
484enum ResolvedName {
485 LibNameSpecified(String),
486 LibNameNotSpecified(String),
488 NoLibTarget,
489}
490
491#[derive(Clone, Debug, Eq, PartialEq, Hash)]
496struct ReqResolvedName<'g> {
497 renamed: Option<String>,
499
500 resolved_name: &'g ResolvedName,
502}
503
504impl<'g> ReqResolvedName<'g> {
505 fn new(renamed: Option<&str>, resolved_name: &'g ResolvedName) -> Self {
506 Self {
507 renamed: renamed.map(|s| s.replace('-', "_")),
508 resolved_name,
509 }
510 }
511
512 fn matches(&self, name: &str) -> bool {
513 if let Some(rename) = &self.renamed {
514 if rename == name {
515 return true;
516 }
517 }
518
519 match self.resolved_name {
520 ResolvedName::LibNameSpecified(resolved_name) => *resolved_name == name,
521 ResolvedName::LibNameNotSpecified(resolved_name) => *resolved_name == name,
522 ResolvedName::NoLibTarget => {
523 name.is_empty()
535 }
536 }
537 }
538}
539
540impl PackageSourceImpl {
541 fn create_path(path: &Utf8Path, workspace_root: &Utf8Path) -> Self {
542 let path_diff =
543 pathdiff::diff_utf8_paths(path, workspace_root).expect("workspace root is absolute");
544 Self::Path(convert_relative_forward_slashes(path_diff).into_boxed_path())
548 }
549}
550
551impl NamedFeatureDep {
552 fn from_cargo_string(input: impl Into<String>) -> Self {
553 let input = input.into();
554 match input.split_once('/') {
555 Some((dep_name, feature)) => {
556 if let Some(dep_name_without_q) = dep_name.strip_suffix('?') {
557 Self::dep_named_feature(dep_name_without_q, feature, true)
558 } else {
559 Self::dep_named_feature(dep_name, feature, false)
560 }
561 }
562 None => match input.strip_prefix("dep:") {
563 Some(dep_name) => Self::optional_dependency(dep_name),
564 None => Self::named_feature(input),
565 },
566 }
567 }
568}
569
570type BuildTargetMap = BTreeMap<OwnedBuildTargetId, BuildTargetImpl>;
571
572struct BuildTargets<'a> {
573 package_id: &'a PackageId,
574 targets: BuildTargetMap,
575}
576
577impl<'a> BuildTargets<'a> {
578 fn new(package_id: &'a PackageId) -> Self {
579 Self {
580 package_id,
581 targets: BTreeMap::new(),
582 }
583 }
584
585 fn add(&mut self, target: Target) -> Result<(), Box<Error>> {
586 use std::collections::btree_map::Entry;
587
588 let mut target_kinds = target
590 .kind
591 .into_iter()
592 .map(|kind| kind.to_string())
593 .collect::<Vec<_>>();
594 let target_name = target.name.into_boxed_str();
595 let crate_types = SortedSet::new(
598 target
599 .crate_types
600 .into_iter()
601 .map(|ct| ct.to_string())
602 .collect::<Vec<_>>(),
603 );
604
605 if target_kinds.len() > 1 && Self::is_proc_macro(&target_kinds) {
607 return Err(Error::PackageGraphConstructError(format!(
608 "for package {}, proc-macro mixed with other kinds ({:?})",
609 self.package_id, target_kinds
610 ))
611 .into());
612 }
613 if crate_types.len() > 1 && Self::is_proc_macro(&crate_types) {
614 return Err(Error::PackageGraphConstructError(format!(
615 "for package {}, proc-macro mixed with other crate types ({})",
616 self.package_id, crate_types
617 ))
618 .into());
619 }
620
621 let (id, kind, lib_name) = if target_kinds.len() > 1 {
622 (
624 OwnedBuildTargetId::Library,
625 BuildTargetKindImpl::LibraryOrExample(crate_types),
626 Some(target_name),
627 )
628 } else if let Some(target_kind) = target_kinds.pop() {
629 let (id, lib_name) = match target_kind.as_str() {
630 "custom-build" => (OwnedBuildTargetId::BuildScript, Some(target_name)),
631 "bin" => (OwnedBuildTargetId::Binary(target_name), None),
632 "example" => (OwnedBuildTargetId::Example(target_name), None),
633 "test" => (OwnedBuildTargetId::Test(target_name), None),
634 "bench" => (OwnedBuildTargetId::Benchmark(target_name), None),
635 _other => {
636 (OwnedBuildTargetId::Library, Some(target_name))
638 }
639 };
640
641 let kind = match &id {
642 OwnedBuildTargetId::Library => {
643 if crate_types.as_slice() == ["proc-macro"] {
644 BuildTargetKindImpl::ProcMacro
645 } else {
646 BuildTargetKindImpl::LibraryOrExample(crate_types)
647 }
648 }
649 OwnedBuildTargetId::Example(_) => {
650 BuildTargetKindImpl::LibraryOrExample(crate_types)
651 }
652 _ => {
653 if crate_types.as_slice() != ["bin"] {
655 return Err(Error::PackageGraphConstructError(format!(
656 "for package {}: build target '{:?}' has invalid crate types '{}'",
657 self.package_id, id, crate_types,
658 ))
659 .into());
660 }
661 BuildTargetKindImpl::Binary
662 }
663 };
664
665 (id, kind, lib_name)
666 } else {
667 return Err(Error::PackageGraphConstructError(format!(
668 "for package ID '{}': build target '{}' has no kinds",
669 self.package_id, target_name
670 ))
671 .into());
672 };
673
674 match self.targets.entry(id) {
675 Entry::Occupied(occupied) => {
676 return Err(Error::PackageGraphConstructError(format!(
677 "for package ID '{}': duplicate build targets for {:?}",
678 self.package_id,
679 occupied.key()
680 ))
681 .into());
682 }
683 Entry::Vacant(vacant) => {
684 vacant.insert(BuildTargetImpl {
685 kind,
686 lib_name,
687 required_features: target.required_features,
688 path: target.src_path.into_boxed_path(),
689 edition: target.edition.to_string().into_boxed_str(),
690 doc_by_default: target.doc,
691 doctest_by_default: target.doctest,
692 test_by_default: target.test,
693 });
694 }
695 }
696
697 Ok(())
698 }
699
700 fn is_proc_macro(list: &[String]) -> bool {
701 list.iter().any(|kind| *kind == "proc-macro")
702 }
703
704 fn finish(self) -> BuildTargetMap {
705 self.targets
706 }
707}
708
709struct DependencyResolver<'g> {
710 from_id: &'g PackageId,
711
712 package_data: &'g AHashMap<PackageId, Rc<PackageDataValue>>,
714
715 dep_reqs: DependencyReqs<'g>,
719}
720
721impl<'g> DependencyResolver<'g> {
722 fn new(
724 from_id: &'g PackageId,
725 package_data: &'g AHashMap<PackageId, Rc<PackageDataValue>>,
726 by_package_name: &'g AHashMap<String, Vec<Rc<PackageDataValue>>>,
727 package_deps: impl IntoIterator<Item = &'g Dependency>,
728 ) -> Self {
729 let mut dep_reqs = DependencyReqs::default();
730 for dep in package_deps {
731 let Some(packages) = by_package_name.get(&dep.name) else {
734 continue;
736 };
737 for package in packages {
738 if cargo_version_matches(&dep.req, &package.version) {
739 let req_resolved_name =
746 ReqResolvedName::new(dep.rename.as_deref(), &package.resolved_name);
747 dep_reqs.push(req_resolved_name, dep);
748 }
749 }
750 }
751
752 Self {
753 from_id,
754 package_data,
755 dep_reqs,
756 }
757 }
758
759 fn resolve<'a>(
762 &'a self,
763 resolved_name: &'a str,
764 dep_id: &PackageId,
765 dep_kinds: &'a [DepKindInfo],
766 ) -> Result<
767 (
768 &'g Rc<PackageDataValue>,
769 impl Iterator<Item = &'g Dependency> + 'a,
770 ),
771 Error,
772 > {
773 let dep_data = self.package_data.get(dep_id).ok_or_else(|| {
774 Error::PackageGraphConstructError(format!(
775 "{}: no package data found for dependency '{}'",
776 self.from_id, dep_id
777 ))
778 })?;
779
780 Ok((
781 dep_data,
782 self.dep_reqs
783 .matches_for(resolved_name, dep_data, dep_kinds),
784 ))
785 }
786}
787
788#[derive(Clone, Debug, Default)]
790struct DependencyReqs<'g> {
791 reqs: Vec<(ReqResolvedName<'g>, &'g Dependency)>,
793}
794
795impl<'g> DependencyReqs<'g> {
796 fn push(&mut self, resolved_name: ReqResolvedName<'g>, dependency: &'g Dependency) {
797 self.reqs.push((resolved_name, dependency));
798 }
799
800 fn matches_for<'a>(
801 &'a self,
802 resolved_name: &'a str,
803 package_data: &'a PackageDataValue,
804 dep_kinds: &'a [DepKindInfo],
805 ) -> impl Iterator<Item = &'g Dependency> + 'a {
806 self.reqs
807 .iter()
808 .filter_map(move |(req_resolved_name, dep)| {
809 if !req_resolved_name.matches(resolved_name) {
815 return None;
816 }
817
818 if !cargo_version_matches(&dep.req, &package_data.version) {
819 return None;
820 }
821
822 if dep_kinds.is_empty() {
826 return Some(*dep);
827 }
828
829 dep_kinds
830 .iter()
831 .any(|dep_kind| dep_kind.kind == dep.kind && dep_kind.target == dep.target)
832 .then_some(*dep)
833 })
834 }
835}
836
837impl PackageLinkImpl {
838 fn new<'a>(
839 from_id: &PackageId,
840 resolved_name: &str,
841 deps: impl IntoIterator<Item = &'a Dependency>,
842 ) -> Result<Self, Box<Error>> {
843 let mut version_req = None;
844 let mut normal = DependencyReqImpl::default();
845 let mut build = DependencyReqImpl::default();
846 let mut dev = DependencyReqImpl::default();
847
848 let mut dep_name: Option<String> = None;
850 for dep in deps {
851 let rename_or_name = dep.rename.as_ref().unwrap_or(&dep.name);
852 match &dep_name {
853 Some(dn) => {
854 if dn != rename_or_name {
855 }
857 }
858 None => {
859 dep_name = Some(rename_or_name.clone());
860 }
861 }
862
863 if dep.kind == DependencyKind::Development && dep.optional {
865 return Err(Error::PackageGraphConstructError(format!(
866 "for package '{}': dev-dependency '{}' marked optional",
867 from_id,
868 dep_name.expect("dep_name set above"),
869 ))
870 .into());
871 }
872
873 if version_req.is_none() {
875 version_req = Some(dep.req.clone());
876 }
877
878 match dep.kind {
879 DependencyKind::Normal => normal.add_instance(from_id, dep)?,
880 DependencyKind::Build => build.add_instance(from_id, dep)?,
881 DependencyKind::Development => dev.add_instance(from_id, dep)?,
882 _ => {
883 continue;
885 }
886 };
887 }
888
889 let dep_name = dep_name.ok_or_else(|| {
890 Error::PackageGraphConstructError(format!(
891 "for package '{}': no dependencies found matching '{}'",
892 from_id, resolved_name,
893 ))
894 })?;
895 let version_req = version_req.unwrap_or_else(|| {
896 panic!(
897 "requires at least one dependency instance: \
898 from `{from_id}` to `{dep_name}` (resolved name `{resolved_name}`)"
899 )
900 });
901
902 Ok(Self {
903 dep_name,
904 resolved_name: resolved_name.into(),
905 version_req,
906 normal,
907 build,
908 dev,
909 })
910 }
911}
912
913impl DependencyReqImpl {
940 fn add_instance(&mut self, from_id: &PackageId, dep: &Dependency) -> Result<(), Box<Error>> {
941 if dep.optional {
942 self.optional.add_instance(from_id, dep)
943 } else {
944 self.required.add_instance(from_id, dep)
945 }
946 }
947}
948
949impl DepRequiredOrOptional {
950 fn add_instance(&mut self, from_id: &PackageId, dep: &Dependency) -> Result<(), Box<Error>> {
951 let target_spec = match dep.target.as_ref() {
953 Some(spec_or_triple) => {
954 let spec_or_triple = format!("{}", spec_or_triple);
956 let target_spec: TargetSpec = spec_or_triple.parse().map_err(|err| {
957 Error::PackageGraphConstructError(format!(
958 "for package '{}': for dependency '{}', parsing target '{}' failed: {}",
959 from_id, dep.name, spec_or_triple, err
960 ))
961 })?;
962 Some(target_spec)
963 }
964 None => None,
965 };
966
967 self.build_if.add_spec(target_spec.as_ref());
968 if dep.uses_default_features {
969 self.default_features_if.add_spec(target_spec.as_ref());
970 } else {
971 self.no_default_features_if.add_spec(target_spec.as_ref());
972 }
973
974 for feature in &dep.features {
975 self.feature_targets
976 .entry(feature.clone())
977 .or_default()
978 .add_spec(target_spec.as_ref());
979 }
980 Ok(())
981 }
982}
983
984impl PackagePublishImpl {
985 fn new(registries: Option<Vec<String>>) -> Self {
987 match registries {
988 None => PackagePublishImpl::Unrestricted,
989 Some(registries) => PackagePublishImpl::Registries(registries.into_boxed_slice()),
990 }
991 }
992}
993
994#[track_caller]
996fn convert_relative_forward_slashes<'a>(rel_path: impl Into<Cow<'a, Utf8Path>>) -> Utf8PathBuf {
997 let rel_path = rel_path.into();
998 cfg_if::cfg_if! { if #[cfg(windows)] {
999
1000 if rel_path.is_relative() {
1001 rel_path.as_str().replace("\\", "/").into()
1002 } else {
1003 rel_path.into_owned()
1004 }
1005
1006 } else {
1007
1008 rel_path.into_owned()
1009
1010 }}
1011}
1012
1013#[cfg(test)]
1014mod tests {
1015 use super::*;
1016
1017 #[test]
1018 fn test_parse_named_feature_dependency() {
1019 assert_eq!(
1020 NamedFeatureDep::from_cargo_string("dep/bar"),
1021 NamedFeatureDep::dep_named_feature("dep", "bar", false),
1022 );
1023 assert_eq!(
1024 NamedFeatureDep::from_cargo_string("dep?/bar"),
1025 NamedFeatureDep::dep_named_feature("dep", "bar", true),
1026 );
1027 assert_eq!(
1028 NamedFeatureDep::from_cargo_string("dep:bar"),
1029 NamedFeatureDep::optional_dependency("bar"),
1030 );
1031 assert_eq!(
1032 NamedFeatureDep::from_cargo_string("foo-bar"),
1033 NamedFeatureDep::named_feature("foo-bar"),
1034 );
1035 }
1036
1037 #[test]
1038 fn test_create_path() {
1039 assert_eq!(
1040 PackageSourceImpl::create_path("/data/foo".as_ref(), "/data/bar".as_ref()),
1041 PackageSourceImpl::Path("../foo".into())
1042 );
1043 assert_eq!(
1044 PackageSourceImpl::create_path("/tmp/foo".as_ref(), "/data/bar".as_ref()),
1045 PackageSourceImpl::Path("../../tmp/foo".into())
1046 );
1047 }
1048
1049 #[test]
1050 fn test_convert_relative_forward_slashes() {
1051 let components = vec!["..", "..", "foo", "bar", "baz.txt"];
1052 let path: Utf8PathBuf = components.into_iter().collect();
1053 let path = convert_relative_forward_slashes(path);
1054 assert_eq!(path.as_str(), "../../foo/bar/baz.txt");
1056 }
1057
1058 #[track_caller]
1059 fn verify_result_of_diff_utf8_paths(
1060 path_manifest: &str,
1061 path_workspace_root: &str,
1062 expected_relative_path: &str,
1063 ) {
1064 let relative_path = pathdiff::diff_utf8_paths(
1065 Utf8Path::new(path_manifest),
1066 Utf8Path::new(path_workspace_root),
1067 )
1068 .unwrap();
1069 assert_eq!(relative_path, expected_relative_path);
1070 }
1071
1072 #[test]
1073 fn test_workspace_path_out_of_pocket() {
1074 verify_result_of_diff_utf8_paths(
1075 "/workspace/a/b/Crate/Cargo.toml",
1076 "/workspace/a/b/.cargo/workspace",
1077 r"../../Crate/Cargo.toml",
1078 );
1079 }
1080
1081 #[cfg(windows)] mod windows {
1083 use super::*;
1084
1085 #[test]
1086 fn test_create_path_windows() {
1087 assert_eq!(
1089 PackageSourceImpl::create_path("C:\\data\\foo".as_ref(), "C:\\data\\bar".as_ref()),
1090 PackageSourceImpl::Path("../foo".into())
1091 );
1092 assert_eq!(
1094 PackageSourceImpl::create_path("D:\\tmp\\foo".as_ref(), "C:\\data\\bar".as_ref()),
1095 PackageSourceImpl::Path("D:\\tmp\\foo".into())
1096 );
1097 }
1098
1099 #[test]
1100 fn test_convert_relative_forward_slashes_absolute() {
1101 let components = vec![r"D:\", "X", "..", "foo", "bar", "baz.txt"];
1102 let path: Utf8PathBuf = components.into_iter().collect();
1103 let path = convert_relative_forward_slashes(path);
1104 assert_eq!(path.as_str(), r"D:\X\..\foo\bar\baz.txt");
1106 }
1107
1108 #[test]
1109 fn test_workspace_path_out_of_pocket_on_windows_same_driver() {
1110 verify_result_of_diff_utf8_paths(
1111 r"C:\workspace\a\b\Crate\Cargo.toml",
1112 r"C:\workspace\a\b\.cargo\workspace",
1113 r"..\..\Crate\Cargo.toml",
1114 );
1115 }
1116
1117 #[test]
1118 fn test_workspace_path_out_of_pocket_on_windows_different_driver() {
1119 verify_result_of_diff_utf8_paths(
1120 r"D:\workspace\a\b\Crate\Cargo.toml",
1121 r"C:\workspace\a\b\.cargo\workspace",
1122 r"D:\workspace\a\b\Crate\Cargo.toml",
1123 );
1124 }
1125 }
1126}