1use std::cell::RefCell;
2use std::collections::hash_map::{Entry, HashMap};
3use std::collections::{BTreeMap, HashSet};
4use std::path::{Path, PathBuf};
5use std::slice;
6
7use glob::glob;
8use log::debug;
9use url::Url;
10
11use crate::core::features::Features;
12use crate::core::registry::PackageRegistry;
13use crate::core::resolver::features::RequestedFeatures;
14use crate::core::{Dependency, PackageId, PackageIdSpec};
15use crate::core::{EitherManifest, Package, SourceId, VirtualManifest};
16use crate::ops;
17use crate::sources::PathSource;
18use crate::util::errors::{CargoResult, CargoResultExt, ManifestError};
19use crate::util::paths;
20use crate::util::toml::{read_manifest, TomlProfiles};
21use crate::util::{Config, Filesystem};
22
23#[derive(Debug)]
29pub struct Workspace<'cfg> {
30 config: &'cfg Config,
31
32 current_manifest: PathBuf,
36
37 packages: Packages<'cfg>,
40
41 root_manifest: Option<PathBuf>,
46
47 target_dir: Option<Filesystem>,
50
51 members: Vec<PathBuf>,
55 member_ids: HashSet<PackageId>,
56
57 default_members: Vec<PathBuf>,
68
69 is_ephemeral: bool,
72
73 require_optional_deps: bool,
78
79 loaded_packages: RefCell<HashMap<PathBuf, Package>>,
82
83 ignore_lock: bool,
86}
87
88#[derive(Debug)]
91struct Packages<'cfg> {
92 config: &'cfg Config,
93 packages: HashMap<PathBuf, MaybePackage>,
94}
95
96#[derive(Debug)]
97enum MaybePackage {
98 Package(Package),
99 Virtual(VirtualManifest),
100}
101
102#[derive(Debug, Clone)]
104pub enum WorkspaceConfig {
105 Root(WorkspaceRootConfig),
108
109 Member { root: Option<String> },
112}
113
114#[derive(Debug, Clone)]
119pub struct WorkspaceRootConfig {
120 root_dir: PathBuf,
121 members: Option<Vec<String>>,
122 default_members: Option<Vec<String>>,
123 exclude: Vec<String>,
124}
125
126pub struct Members<'a, 'cfg> {
129 ws: &'a Workspace<'cfg>,
130 iter: slice::Iter<'a, PathBuf>,
131}
132
133impl<'cfg> Workspace<'cfg> {
134 pub fn new(manifest_path: &Path, config: &'cfg Config) -> CargoResult<Workspace<'cfg>> {
141 let mut ws = Workspace::new_default(manifest_path.to_path_buf(), config);
142 ws.target_dir = config.target_dir()?;
143 ws.root_manifest = ws.find_root(manifest_path)?;
144 ws.find_members()?;
145 ws.validate()?;
146 Ok(ws)
147 }
148
149 fn new_default(current_manifest: PathBuf, config: &'cfg Config) -> Workspace<'cfg> {
150 Workspace {
151 config,
152 current_manifest,
153 packages: Packages {
154 config,
155 packages: HashMap::new(),
156 },
157 root_manifest: None,
158 target_dir: None,
159 members: Vec::new(),
160 member_ids: HashSet::new(),
161 default_members: Vec::new(),
162 is_ephemeral: false,
163 require_optional_deps: true,
164 loaded_packages: RefCell::new(HashMap::new()),
165 ignore_lock: false,
166 }
167 }
168
169 pub fn new_virtual(
170 root_path: PathBuf,
171 current_manifest: PathBuf,
172 manifest: VirtualManifest,
173 config: &'cfg Config,
174 ) -> CargoResult<Workspace<'cfg>> {
175 let mut ws = Workspace::new_default(current_manifest, config);
176 ws.root_manifest = Some(root_path.join("Cargo.toml"));
177 ws.target_dir = config.target_dir()?;
178 ws.packages
179 .packages
180 .insert(root_path, MaybePackage::Virtual(manifest));
181 ws.find_members()?;
182 Ok(ws)
185 }
186
187 pub fn ephemeral(
197 package: Package,
198 config: &'cfg Config,
199 target_dir: Option<Filesystem>,
200 require_optional_deps: bool,
201 ) -> CargoResult<Workspace<'cfg>> {
202 let mut ws = Workspace::new_default(package.manifest_path().to_path_buf(), config);
203 ws.is_ephemeral = true;
204 ws.require_optional_deps = require_optional_deps;
205 let key = ws.current_manifest.parent().unwrap();
206 let id = package.package_id();
207 let package = MaybePackage::Package(package);
208 ws.packages.packages.insert(key.to_path_buf(), package);
209 ws.target_dir = if let Some(dir) = target_dir {
210 Some(dir)
211 } else {
212 ws.config.target_dir()?
213 };
214 ws.members.push(ws.current_manifest.clone());
215 ws.member_ids.insert(id);
216 ws.default_members.push(ws.current_manifest.clone());
217 Ok(ws)
218 }
219
220 pub fn current(&self) -> CargoResult<&Package> {
226 let pkg = self.current_opt().ok_or_else(|| {
227 anyhow::format_err!(
228 "manifest path `{}` is a virtual manifest, but this \
229 command requires running against an actual package in \
230 this workspace",
231 self.current_manifest.display()
232 )
233 })?;
234 Ok(pkg)
235 }
236
237 pub fn current_mut(&mut self) -> CargoResult<&mut Package> {
238 let cm = self.current_manifest.clone();
239 let pkg = self.current_opt_mut().ok_or_else(|| {
240 anyhow::format_err!(
241 "manifest path `{}` is a virtual manifest, but this \
242 command requires running against an actual package in \
243 this workspace",
244 cm.display()
245 )
246 })?;
247 Ok(pkg)
248 }
249
250 pub fn current_opt(&self) -> Option<&Package> {
251 match *self.packages.get(&self.current_manifest) {
252 MaybePackage::Package(ref p) => Some(p),
253 MaybePackage::Virtual(..) => None,
254 }
255 }
256
257 pub fn current_opt_mut(&mut self) -> Option<&mut Package> {
258 match *self.packages.get_mut(&self.current_manifest) {
259 MaybePackage::Package(ref mut p) => Some(p),
260 MaybePackage::Virtual(..) => None,
261 }
262 }
263
264 pub fn is_virtual(&self) -> bool {
265 match *self.packages.get(&self.current_manifest) {
266 MaybePackage::Package(..) => false,
267 MaybePackage::Virtual(..) => true,
268 }
269 }
270
271 pub fn config(&self) -> &'cfg Config {
273 self.config
274 }
275
276 pub fn profiles(&self) -> Option<&TomlProfiles> {
277 match self.root_maybe() {
278 MaybePackage::Package(p) => p.manifest().profiles(),
279 MaybePackage::Virtual(vm) => vm.profiles(),
280 }
281 }
282
283 pub fn root(&self) -> &Path {
288 match self.root_manifest {
289 Some(ref p) => p,
290 None => &self.current_manifest,
291 }
292 .parent()
293 .unwrap()
294 }
295
296 fn root_maybe(&self) -> &MaybePackage {
298 let root = self
299 .root_manifest
300 .as_ref()
301 .unwrap_or(&self.current_manifest);
302 self.packages.get(root)
303 }
304
305 pub fn target_dir(&self) -> Filesystem {
306 self.target_dir
307 .clone()
308 .unwrap_or_else(|| Filesystem::new(self.root().join("target")))
309 }
310
311 pub fn root_replace(&self) -> &[(PackageIdSpec, Dependency)] {
315 match self.root_maybe() {
316 MaybePackage::Package(p) => p.manifest().replace(),
317 MaybePackage::Virtual(vm) => vm.replace(),
318 }
319 }
320
321 pub fn root_patch(&self) -> &HashMap<Url, Vec<Dependency>> {
325 match self.root_maybe() {
326 MaybePackage::Package(p) => p.manifest().patch(),
327 MaybePackage::Virtual(vm) => vm.patch(),
328 }
329 }
330
331 pub fn members<'a>(&'a self) -> Members<'a, 'cfg> {
333 Members {
334 ws: self,
335 iter: self.members.iter(),
336 }
337 }
338
339 pub fn default_members<'a>(&'a self) -> Members<'a, 'cfg> {
341 Members {
342 ws: self,
343 iter: self.default_members.iter(),
344 }
345 }
346
347 pub fn is_member(&self, pkg: &Package) -> bool {
349 self.member_ids.contains(&pkg.package_id())
350 }
351
352 pub fn is_ephemeral(&self) -> bool {
353 self.is_ephemeral
354 }
355
356 pub fn require_optional_deps(&self) -> bool {
357 self.require_optional_deps
358 }
359
360 pub fn set_require_optional_deps(
361 &mut self,
362 require_optional_deps: bool,
363 ) -> &mut Workspace<'cfg> {
364 self.require_optional_deps = require_optional_deps;
365 self
366 }
367
368 pub fn ignore_lock(&self) -> bool {
369 self.ignore_lock
370 }
371
372 pub fn set_ignore_lock(&mut self, ignore_lock: bool) -> &mut Workspace<'cfg> {
373 self.ignore_lock = ignore_lock;
374 self
375 }
376
377 fn find_root(&mut self, manifest_path: &Path) -> CargoResult<Option<PathBuf>> {
387 fn read_root_pointer(member_manifest: &Path, root_link: &str) -> CargoResult<PathBuf> {
388 let path = member_manifest
389 .parent()
390 .unwrap()
391 .join(root_link)
392 .join("Cargo.toml");
393 debug!("find_root - pointer {}", path.display());
394 Ok(paths::normalize_path(&path))
395 };
396
397 {
398 let current = self.packages.load(manifest_path)?;
399 match *current.workspace_config() {
400 WorkspaceConfig::Root(_) => {
401 debug!("find_root - is root {}", manifest_path.display());
402 return Ok(Some(manifest_path.to_path_buf()));
403 }
404 WorkspaceConfig::Member {
405 root: Some(ref path_to_root),
406 } => return Ok(Some(read_root_pointer(manifest_path, path_to_root)?)),
407 WorkspaceConfig::Member { root: None } => {}
408 }
409 }
410
411 for path in paths::ancestors(manifest_path).skip(2) {
412 if path.ends_with("target/package") {
413 break;
414 }
415
416 let ances_manifest_path = path.join("Cargo.toml");
417 debug!("find_root - trying {}", ances_manifest_path.display());
418 if ances_manifest_path.exists() {
419 match *self.packages.load(&ances_manifest_path)?.workspace_config() {
420 WorkspaceConfig::Root(ref ances_root_config) => {
421 debug!("find_root - found a root checking exclusion");
422 if !ances_root_config.is_excluded(manifest_path) {
423 debug!("find_root - found!");
424 return Ok(Some(ances_manifest_path));
425 }
426 }
427 WorkspaceConfig::Member {
428 root: Some(ref path_to_root),
429 } => {
430 debug!("find_root - found pointer");
431 return Ok(Some(read_root_pointer(&ances_manifest_path, path_to_root)?));
432 }
433 WorkspaceConfig::Member { .. } => {}
434 }
435 }
436
437 if self.config.home() == path {
443 break;
444 }
445 }
446
447 Ok(None)
448 }
449
450 fn find_members(&mut self) -> CargoResult<()> {
458 let root_manifest_path = match self.root_manifest {
459 Some(ref path) => path.clone(),
460 None => {
461 debug!("find_members - only me as a member");
462 self.members.push(self.current_manifest.clone());
463 self.default_members.push(self.current_manifest.clone());
464 if let Ok(pkg) = self.current() {
465 let id = pkg.package_id();
466 self.member_ids.insert(id);
467 }
468 return Ok(());
469 }
470 };
471
472 let members_paths;
473 let default_members_paths;
474 {
475 let root_package = self.packages.load(&root_manifest_path)?;
476 match *root_package.workspace_config() {
477 WorkspaceConfig::Root(ref root_config) => {
478 members_paths = root_config
479 .members_paths(root_config.members.as_ref().unwrap_or(&vec![]))?;
480 default_members_paths = if root_manifest_path == self.current_manifest {
481 if let Some(ref default) = root_config.default_members {
482 Some(root_config.members_paths(default)?)
483 } else {
484 None
485 }
486 } else {
487 None
488 };
489 }
490 _ => anyhow::bail!(
491 "root of a workspace inferred but wasn't a root: {}",
492 root_manifest_path.display()
493 ),
494 }
495 }
496
497 for path in members_paths {
498 self.find_path_deps(&path.join("Cargo.toml"), &root_manifest_path, false)?;
499 }
500
501 if let Some(default) = default_members_paths {
502 for path in default {
503 let manifest_path = paths::normalize_path(&path.join("Cargo.toml"));
504 if !self.members.contains(&manifest_path) {
505 anyhow::bail!(
506 "package `{}` is listed in workspace’s default-members \
507 but is not a member.",
508 path.display()
509 )
510 }
511 self.default_members.push(manifest_path)
512 }
513 } else if self.is_virtual() {
514 self.default_members = self.members.clone()
515 } else {
516 self.default_members.push(self.current_manifest.clone())
517 }
518
519 self.find_path_deps(&root_manifest_path, &root_manifest_path, false)
520 }
521
522 fn find_path_deps(
523 &mut self,
524 manifest_path: &Path,
525 root_manifest: &Path,
526 is_path_dep: bool,
527 ) -> CargoResult<()> {
528 let manifest_path = paths::normalize_path(manifest_path);
529 if self.members.contains(&manifest_path) {
530 return Ok(());
531 }
532 if is_path_dep
533 && !manifest_path.parent().unwrap().starts_with(self.root())
534 && self.find_root(&manifest_path)? != self.root_manifest
535 {
536 return Ok(());
539 }
540
541 if let WorkspaceConfig::Root(ref root_config) =
542 *self.packages.load(root_manifest)?.workspace_config()
543 {
544 if root_config.is_excluded(&manifest_path) {
545 return Ok(());
546 }
547 }
548
549 debug!("find_members - {}", manifest_path.display());
550 self.members.push(manifest_path.clone());
551
552 let candidates = {
553 let pkg = match *self.packages.load(&manifest_path)? {
554 MaybePackage::Package(ref p) => p,
555 MaybePackage::Virtual(_) => return Ok(()),
556 };
557 self.member_ids.insert(pkg.package_id());
558 pkg.dependencies()
559 .iter()
560 .map(|d| d.source_id())
561 .filter(|d| d.is_path())
562 .filter_map(|d| d.url().to_file_path().ok())
563 .map(|p| p.join("Cargo.toml"))
564 .collect::<Vec<_>>()
565 };
566 for candidate in candidates {
567 self.find_path_deps(&candidate, root_manifest, true)
568 .map_err(|err| ManifestError::new(err, manifest_path.clone()))?;
569 }
570 Ok(())
571 }
572
573 pub fn features(&self) -> &Features {
574 match self.root_maybe() {
575 MaybePackage::Package(p) => p.manifest().features(),
576 MaybePackage::Virtual(vm) => vm.features(),
577 }
578 }
579
580 fn validate(&mut self) -> CargoResult<()> {
586 if self.root_manifest.is_none() {
588 return Ok(());
589 }
590
591 self.validate_unique_names()?;
592 self.validate_workspace_roots()?;
593 self.validate_members()?;
594 self.error_if_manifest_not_in_members()?;
595 self.validate_manifest()
596 }
597
598 fn validate_unique_names(&self) -> CargoResult<()> {
599 let mut names = BTreeMap::new();
600 for member in self.members.iter() {
601 let package = self.packages.get(member);
602 let name = match *package {
603 MaybePackage::Package(ref p) => p.name(),
604 MaybePackage::Virtual(_) => continue,
605 };
606 if let Some(prev) = names.insert(name, member) {
607 anyhow::bail!(
608 "two packages named `{}` in this workspace:\n\
609 - {}\n\
610 - {}",
611 name,
612 prev.display(),
613 member.display()
614 );
615 }
616 }
617 Ok(())
618 }
619
620 fn validate_workspace_roots(&self) -> CargoResult<()> {
621 let roots: Vec<PathBuf> = self
622 .members
623 .iter()
624 .filter(|&member| {
625 let config = self.packages.get(member).workspace_config();
626 matches!(config, WorkspaceConfig::Root(_))
627 })
628 .map(|member| member.parent().unwrap().to_path_buf())
629 .collect();
630 match roots.len() {
631 1 => Ok(()),
632 0 => anyhow::bail!(
633 "`package.workspace` configuration points to a crate \
634 which is not configured with [workspace]: \n\
635 configuration at: {}\n\
636 points to: {}",
637 self.current_manifest.display(),
638 self.root_manifest.as_ref().unwrap().display()
639 ),
640 _ => {
641 anyhow::bail!(
642 "multiple workspace roots found in the same workspace:\n{}",
643 roots
644 .iter()
645 .map(|r| format!(" {}", r.display()))
646 .collect::<Vec<_>>()
647 .join("\n")
648 );
649 }
650 }
651 }
652
653 fn validate_members(&mut self) -> CargoResult<()> {
654 for member in self.members.clone() {
655 let root = self.find_root(&member)?;
656 if root == self.root_manifest {
657 continue;
658 }
659
660 match root {
661 Some(root) => {
662 anyhow::bail!(
663 "package `{}` is a member of the wrong workspace\n\
664 expected: {}\n\
665 actual: {}",
666 member.display(),
667 self.root_manifest.as_ref().unwrap().display(),
668 root.display()
669 );
670 }
671 None => {
672 anyhow::bail!(
673 "workspace member `{}` is not hierarchically below \
674 the workspace root `{}`",
675 member.display(),
676 self.root_manifest.as_ref().unwrap().display()
677 );
678 }
679 }
680 }
681 Ok(())
682 }
683
684 fn error_if_manifest_not_in_members(&mut self) -> CargoResult<()> {
685 if self.members.contains(&self.current_manifest) {
686 return Ok(());
687 }
688
689 let root = self.root_manifest.as_ref().unwrap();
690 let root_dir = root.parent().unwrap();
691 let current_dir = self.current_manifest.parent().unwrap();
692 let root_pkg = self.packages.get(root);
693
694 let members_msg = match current_dir.strip_prefix(root_dir) {
696 Ok(rel) => format!(
697 "this may be fixable by adding `{}` to the \
698 `workspace.members` array of the manifest \
699 located at: {}",
700 rel.display(),
701 root.display()
702 ),
703 Err(_) => format!(
704 "this may be fixable by adding a member to \
705 the `workspace.members` array of the \
706 manifest located at: {}",
707 root.display()
708 ),
709 };
710 let extra = match *root_pkg {
711 MaybePackage::Virtual(_) => members_msg,
712 MaybePackage::Package(ref p) => {
713 let has_members_list = match *p.manifest().workspace_config() {
714 WorkspaceConfig::Root(ref root_config) => root_config.has_members_list(),
715 WorkspaceConfig::Member { .. } => unreachable!(),
716 };
717 if !has_members_list {
718 format!(
719 "this may be fixable by ensuring that this \
720 crate is depended on by the workspace \
721 root: {}",
722 root.display()
723 )
724 } else {
725 members_msg
726 }
727 }
728 };
729 anyhow::bail!(
730 "current package believes it's in a workspace when it's not:\n\
731 current: {}\n\
732 workspace: {}\n\n{}\n\
733 Alternatively, to keep it out of the workspace, add the package \
734 to the `workspace.exclude` array, or add an empty `[workspace]` \
735 table to the package's manifest.",
736 self.current_manifest.display(),
737 root.display(),
738 extra
739 );
740 }
741
742 fn validate_manifest(&mut self) -> CargoResult<()> {
743 if let Some(ref root_manifest) = self.root_manifest {
744 for pkg in self
745 .members()
746 .filter(|p| p.manifest_path() != root_manifest)
747 {
748 let manifest = pkg.manifest();
749 let emit_warning = |what| -> CargoResult<()> {
750 let msg = format!(
751 "{} for the non root package will be ignored, \
752 specify {} at the workspace root:\n\
753 package: {}\n\
754 workspace: {}",
755 what,
756 what,
757 pkg.manifest_path().display(),
758 root_manifest.display(),
759 );
760 self.config.shell().warn(&msg)
761 };
762 if manifest.original().has_profiles() {
763 emit_warning("profiles")?;
764 }
765 if !manifest.replace().is_empty() {
766 emit_warning("replace")?;
767 }
768 if !manifest.patch().is_empty() {
769 emit_warning("patch")?;
770 }
771 }
772 }
773 Ok(())
774 }
775
776 pub fn load(&self, manifest_path: &Path) -> CargoResult<Package> {
777 match self.packages.maybe_get(manifest_path) {
778 Some(&MaybePackage::Package(ref p)) => return Ok(p.clone()),
779 Some(&MaybePackage::Virtual(_)) => anyhow::bail!("cannot load workspace root"),
780 None => {}
781 }
782
783 let mut loaded = self.loaded_packages.borrow_mut();
784 if let Some(p) = loaded.get(manifest_path).cloned() {
785 return Ok(p);
786 }
787 let source_id = SourceId::for_path(manifest_path.parent().unwrap())?;
788 let (package, _nested_paths) = ops::read_package(manifest_path, source_id, self.config)?;
789 loaded.insert(manifest_path.to_path_buf(), package.clone());
790 Ok(package)
791 }
792
793 pub fn preload(&self, registry: &mut PackageRegistry<'cfg>) {
800 if self.is_ephemeral {
806 return;
807 }
808
809 for pkg in self.packages.packages.values() {
810 let pkg = match *pkg {
811 MaybePackage::Package(ref p) => p.clone(),
812 MaybePackage::Virtual(_) => continue,
813 };
814 let mut src = PathSource::new(
815 pkg.manifest_path(),
816 pkg.package_id().source_id(),
817 self.config,
818 );
819 src.preload_with(pkg);
820 registry.add_preloaded(Box::new(src));
821 }
822 }
823
824 pub fn emit_warnings(&self) -> CargoResult<()> {
825 for (path, maybe_pkg) in &self.packages.packages {
826 let warnings = match maybe_pkg {
827 MaybePackage::Package(pkg) => pkg.manifest().warnings().warnings(),
828 MaybePackage::Virtual(vm) => vm.warnings().warnings(),
829 };
830 let path = path.join("Cargo.toml");
831 for warning in warnings {
832 if warning.is_critical {
833 let err = anyhow::format_err!("{}", warning.message);
834 let cx =
835 anyhow::format_err!("failed to parse manifest at `{}`", path.display());
836 return Err(err.context(cx).into());
837 } else {
838 let msg = if self.root_manifest.is_none() {
839 warning.message.to_string()
840 } else {
841 format!("{}: {}", path.display(), warning.message)
844 };
845 self.config.shell().warn(msg)?
846 }
847 }
848 }
849 Ok(())
850 }
851
852 pub fn set_target_dir(&mut self, target_dir: Filesystem) {
853 self.target_dir = Some(target_dir);
854 }
855
856 pub fn members_with_features(
864 &self,
865 specs: &[PackageIdSpec],
866 requested_features: &RequestedFeatures,
867 ) -> CargoResult<Vec<(&Package, RequestedFeatures)>> {
868 assert!(
869 !specs.is_empty() || requested_features.all_features,
870 "no specs requires all_features"
871 );
872 if specs.is_empty() {
873 return Ok(self
876 .members()
877 .map(|m| (m, RequestedFeatures::new_all(true)))
878 .collect());
879 }
880 if self.config().cli_unstable().package_features {
881 if specs.len() > 1 && !requested_features.features.is_empty() {
882 anyhow::bail!("cannot specify features for more than one package");
883 }
884 let members: Vec<(&Package, RequestedFeatures)> = self
885 .members()
886 .filter(|m| specs.iter().any(|spec| spec.matches(m.package_id())))
887 .map(|m| (m, requested_features.clone()))
888 .collect();
889 if members.is_empty() {
890 if !(requested_features.features.is_empty()
893 && !requested_features.all_features
894 && requested_features.uses_default_features)
895 {
896 anyhow::bail!("cannot specify features for packages outside of workspace");
897 }
898 return Ok(self
901 .members()
902 .map(|m| (m, RequestedFeatures::new_all(false)))
903 .collect());
904 }
905 Ok(members)
906 } else {
907 let ms = self.members().filter_map(|member| {
908 let member_id = member.package_id();
909 match self.current_opt() {
910 Some(current) if member_id == current.package_id() => {
913 Some((member, requested_features.clone()))
914 }
915 _ => {
916 if specs.iter().any(|spec| spec.matches(member_id)) {
918 Some((
922 member,
923 RequestedFeatures::new_all(requested_features.all_features),
924 ))
925 } else {
926 None
928 }
929 }
930 }
931 });
932 Ok(ms.collect())
933 }
934 }
935}
936
937impl<'cfg> Packages<'cfg> {
938 fn get(&self, manifest_path: &Path) -> &MaybePackage {
939 self.maybe_get(manifest_path).unwrap()
940 }
941
942 fn get_mut(&mut self, manifest_path: &Path) -> &mut MaybePackage {
943 self.maybe_get_mut(manifest_path).unwrap()
944 }
945
946 fn maybe_get(&self, manifest_path: &Path) -> Option<&MaybePackage> {
947 self.packages.get(manifest_path.parent().unwrap())
948 }
949
950 fn maybe_get_mut(&mut self, manifest_path: &Path) -> Option<&mut MaybePackage> {
951 self.packages.get_mut(manifest_path.parent().unwrap())
952 }
953
954 fn load(&mut self, manifest_path: &Path) -> CargoResult<&MaybePackage> {
955 let key = manifest_path.parent().unwrap();
956 match self.packages.entry(key.to_path_buf()) {
957 Entry::Occupied(e) => Ok(e.into_mut()),
958 Entry::Vacant(v) => {
959 let source_id = SourceId::for_path(key)?;
960 let (manifest, _nested_paths) =
961 read_manifest(manifest_path, source_id, self.config)?;
962 Ok(v.insert(match manifest {
963 EitherManifest::Real(manifest) => {
964 MaybePackage::Package(Package::new(manifest, manifest_path))
965 }
966 EitherManifest::Virtual(vm) => MaybePackage::Virtual(vm),
967 }))
968 }
969 }
970 }
971}
972
973impl<'a, 'cfg> Iterator for Members<'a, 'cfg> {
974 type Item = &'a Package;
975
976 fn next(&mut self) -> Option<&'a Package> {
977 loop {
978 let next = self.iter.next().map(|path| self.ws.packages.get(path));
979 match next {
980 Some(&MaybePackage::Package(ref p)) => return Some(p),
981 Some(&MaybePackage::Virtual(_)) => {}
982 None => return None,
983 }
984 }
985 }
986
987 fn size_hint(&self) -> (usize, Option<usize>) {
988 let (_, upper) = self.iter.size_hint();
989 (0, upper)
990 }
991}
992
993impl MaybePackage {
994 fn workspace_config(&self) -> &WorkspaceConfig {
995 match *self {
996 MaybePackage::Package(ref p) => p.manifest().workspace_config(),
997 MaybePackage::Virtual(ref vm) => vm.workspace_config(),
998 }
999 }
1000}
1001
1002impl WorkspaceRootConfig {
1003 pub fn new(
1005 root_dir: &Path,
1006 members: &Option<Vec<String>>,
1007 default_members: &Option<Vec<String>>,
1008 exclude: &Option<Vec<String>>,
1009 ) -> WorkspaceRootConfig {
1010 WorkspaceRootConfig {
1011 root_dir: root_dir.to_path_buf(),
1012 members: members.clone(),
1013 default_members: default_members.clone(),
1014 exclude: exclude.clone().unwrap_or_default(),
1015 }
1016 }
1017
1018 fn is_excluded(&self, manifest_path: &Path) -> bool {
1022 let excluded = self
1023 .exclude
1024 .iter()
1025 .any(|ex| manifest_path.starts_with(self.root_dir.join(ex)));
1026
1027 let explicit_member = match self.members {
1028 Some(ref members) => members
1029 .iter()
1030 .any(|mem| manifest_path.starts_with(self.root_dir.join(mem))),
1031 None => false,
1032 };
1033
1034 !explicit_member && excluded
1035 }
1036
1037 fn has_members_list(&self) -> bool {
1038 self.members.is_some()
1039 }
1040
1041 fn members_paths(&self, globs: &[String]) -> CargoResult<Vec<PathBuf>> {
1042 let mut expanded_list = Vec::new();
1043
1044 for glob in globs {
1045 let pathbuf = self.root_dir.join(glob);
1046 let expanded_paths = Self::expand_member_path(&pathbuf)?;
1047
1048 if expanded_paths.is_empty() {
1051 expanded_list.push(pathbuf);
1052 } else {
1053 expanded_list.extend(expanded_paths);
1054 }
1055 }
1056
1057 Ok(expanded_list)
1058 }
1059
1060 fn expand_member_path(path: &Path) -> CargoResult<Vec<PathBuf>> {
1061 let path = match path.to_str() {
1062 Some(p) => p,
1063 None => return Ok(Vec::new()),
1064 };
1065 let res =
1066 glob(path).chain_err(|| anyhow::format_err!("could not parse pattern `{}`", &path))?;
1067 let res = res
1068 .map(|p| {
1069 p.chain_err(|| anyhow::format_err!("unable to match path to pattern `{}`", &path))
1070 })
1071 .collect::<Result<Vec<_>, _>>()?;
1072 Ok(res)
1073 }
1074}